Commit 63547b06 authored by Urban Widmark's avatar Urban Widmark Committed by Linus Torvalds

[PATCH] smbfs unicode support

This patch adds unicode support and wants to be applied on top of the LFS
one. It uses a fake nls module to do the (little endian) unicode
translation.
parent de8999dc
ChangeLog for smbfs. ChangeLog for smbfs.
2001-08-03 Urban Widmark <urban@teststation.com>
* *.c: Unicode support
2001-08-23 Jochen Dolze <dolze@epcnet.de> 2001-08-23 Jochen Dolze <dolze@epcnet.de>
* proc.c: Correct rsize/wsize computation for readX/writeX * proc.c: Correct rsize/wsize computation for readX/writeX
......
...@@ -111,8 +111,8 @@ static void reverse_string(char *buf, int len) ...@@ -111,8 +111,8 @@ static void reverse_string(char *buf, int len)
} }
/* no conversion, just a wrapper for memcpy. */ /* no conversion, just a wrapper for memcpy. */
static int convert_memcpy(char *output, int olen, static int convert_memcpy(unsigned char *output, int olen,
const char *input, int ilen, const unsigned char *input, int ilen,
struct nls_table *nls_from, struct nls_table *nls_from,
struct nls_table *nls_to) struct nls_table *nls_to)
{ {
...@@ -139,8 +139,8 @@ static inline int write_unichar(wchar_t ch, char *output, int olen) ...@@ -139,8 +139,8 @@ static inline int write_unichar(wchar_t ch, char *output, int olen)
} }
/* convert from one "codepage" to another (possibly being utf8). */ /* convert from one "codepage" to another (possibly being utf8). */
static int convert_cp(char *output, int olen, static int convert_cp(unsigned char *output, int olen,
const char *input, int ilen, const unsigned char *input, int ilen,
struct nls_table *nls_from, struct nls_table *nls_from,
struct nls_table *nls_to) struct nls_table *nls_to)
{ {
...@@ -150,7 +150,7 @@ static int convert_cp(char *output, int olen, ...@@ -150,7 +150,7 @@ static int convert_cp(char *output, int olen,
while (ilen > 0) { while (ilen > 0) {
/* convert by changing to unicode and back to the new cp */ /* convert by changing to unicode and back to the new cp */
n = nls_from->char2uni((unsigned char *)input, ilen, &ch); n = nls_from->char2uni(input, ilen, &ch);
if (n == -EINVAL) { if (n == -EINVAL) {
ilen--; ilen--;
n = write_char(*input++, output, olen); n = write_char(*input++, output, olen);
...@@ -180,6 +180,42 @@ static int convert_cp(char *output, int olen, ...@@ -180,6 +180,42 @@ static int convert_cp(char *output, int olen,
return n; return n;
} }
/* ----------------------------------------------------------- */
/*
* nls_unicode
*
* This encodes/decodes little endian unicode format
*/
static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
{
if (boundlen < 2)
return -EINVAL;
*out++ = uni & 0xff;
*out++ = uni >> 8;
return 2;
}
static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
{
if (boundlen < 2)
return -EINVAL;
*uni = (rawstring[1] << 8) | rawstring[0];
return 2;
}
static struct nls_table unicode_table = {
"unicode",
uni2char,
char2uni,
NULL, /* not used by smbfs */
NULL,
NULL, /* not a module */
};
/* ----------------------------------------------------------- */
static int setcodepage(struct nls_table **p, char *name) static int setcodepage(struct nls_table **p, char *name)
{ {
struct nls_table *nls; struct nls_table *nls;
...@@ -192,7 +228,7 @@ static int setcodepage(struct nls_table **p, char *name) ...@@ -192,7 +228,7 @@ static int setcodepage(struct nls_table **p, char *name)
} }
/* if already set, unload the previous one. */ /* if already set, unload the previous one. */
if (*p) if (*p && *p != &unicode_table)
unload_nls(*p); unload_nls(*p);
*p = nls; *p = nls;
...@@ -210,12 +246,19 @@ int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp) ...@@ -210,12 +246,19 @@ int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp)
if (!*cp->remote_name) if (!*cp->remote_name)
goto out; goto out;
/* local */
n = setcodepage(&server->local_nls, cp->local_name); n = setcodepage(&server->local_nls, cp->local_name);
if (n != 0) if (n != 0)
goto out; goto out;
n = setcodepage(&server->remote_nls, cp->remote_name);
if (n != 0) /* remote */
setcodepage(&server->local_nls, NULL); if (!strcmp(cp->remote_name, "unicode")) {
server->remote_nls = &unicode_table;
} else {
n = setcodepage(&server->remote_nls, cp->remote_name);
if (n != 0)
setcodepage(&server->local_nls, NULL);
}
out: out:
if (server->local_nls != NULL && server->remote_nls != NULL) if (server->local_nls != NULL && server->remote_nls != NULL)
...@@ -252,13 +295,15 @@ smb_encode_smb_length(__u8 * p, __u32 len) ...@@ -252,13 +295,15 @@ smb_encode_smb_length(__u8 * p, __u32 len)
* smb_build_path: build the path to entry and name storing it in buf. * smb_build_path: build the path to entry and name storing it in buf.
* The path returned will have the trailing '\0'. * The path returned will have the trailing '\0'.
*/ */
static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen, static int smb_build_path(struct smb_sb_info *server, unsigned char *buf,
struct dentry * entry, struct qstr * name) int maxlen,
struct dentry *entry, struct qstr *name)
{ {
char *path = buf; unsigned char *path = buf;
int len; int len;
int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE) != 0;
if (maxlen < 2) if (maxlen < (2<<unicode))
return -ENAMETOOLONG; return -ENAMETOOLONG;
if (maxlen > SMB_MAXPATHLEN + 1) if (maxlen > SMB_MAXPATHLEN + 1)
...@@ -272,8 +317,10 @@ static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen, ...@@ -272,8 +317,10 @@ static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen,
*/ */
if (IS_ROOT(entry) && !name) { if (IS_ROOT(entry) && !name) {
*path++ = '\\'; *path++ = '\\';
if (unicode) *path++ = '\0';
*path++ = '\0'; *path++ = '\0';
return 2; if (unicode) *path++ = '\0';
return path-buf;
} }
/* /*
...@@ -282,7 +329,7 @@ static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen, ...@@ -282,7 +329,7 @@ static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen,
*/ */
read_lock(&dparent_lock); read_lock(&dparent_lock);
while (!IS_ROOT(entry)) { while (!IS_ROOT(entry)) {
if (maxlen < 3) { if (maxlen < (3<<unicode)) {
read_unlock(&dparent_lock); read_unlock(&dparent_lock);
return -ENAMETOOLONG; return -ENAMETOOLONG;
} }
...@@ -296,22 +343,29 @@ static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen, ...@@ -296,22 +343,29 @@ static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen,
} }
reverse_string(path, len); reverse_string(path, len);
path += len; path += len;
if (unicode) {
/* Note: reverse order */
*path++ = '\0';
maxlen--;
}
*path++ = '\\'; *path++ = '\\';
maxlen -= len+1; maxlen -= len+1;
entry = entry->d_parent; entry = entry->d_parent;
if (IS_ROOT(entry))
break;
} }
read_unlock(&dparent_lock); read_unlock(&dparent_lock);
reverse_string(buf, path-buf); reverse_string(buf, path-buf);
/* maxlen is at least 1 */ /* maxlen has space for at least one char */
test_name_and_out: test_name_and_out:
if (name) { if (name) {
if (maxlen < 3) if (maxlen < (3<<unicode))
return -ENAMETOOLONG; return -ENAMETOOLONG;
*path++ = '\\'; *path++ = '\\';
if (unicode) {
*path++ = '\0';
maxlen--;
}
len = server->ops->convert(path, maxlen-2, len = server->ops->convert(path, maxlen-2,
name->name, name->len, name->name, name->len,
server->local_nls, server->remote_nls); server->local_nls, server->remote_nls);
...@@ -320,8 +374,9 @@ static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen, ...@@ -320,8 +374,9 @@ static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen,
path += len; path += len;
maxlen -= len+1; maxlen -= len+1;
} }
/* maxlen is at least 1 */ /* maxlen has space for at least one char */
*path++ = '\0'; *path++ = '\0';
if (unicode) *path++ = '\0';
return path-buf; return path-buf;
} }
...@@ -339,16 +394,31 @@ static int smb_encode_path(struct smb_sb_info *server, char *buf, int maxlen, ...@@ -339,16 +394,31 @@ static int smb_encode_path(struct smb_sb_info *server, char *buf, int maxlen,
return result; return result;
} }
/* encode_path for non-trans2 request SMBs */
static int smb_simple_encode_path(struct smb_sb_info *server, char **p, static int smb_simple_encode_path(struct smb_sb_info *server, char **p,
struct dentry * entry, struct qstr * name) struct dentry * entry, struct qstr * name)
{ {
char *s = *p; char *s = *p;
int res; int res;
int maxlen = ((char *)server->packet + server->packet_size) - s; int maxlen = ((char *)server->packet + server->packet_size) - s;
int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE);
if (!maxlen) if (!maxlen)
return -ENAMETOOLONG; return -ENAMETOOLONG;
*s++ = 4; *s++ = 4; /* ASCII data format */
/*
* SMB Unicode strings must be 16bit aligned relative the start of the
* packet. If they are not they must be padded with 0.
*/
if (unicode) {
int align = s - (char *)server->packet;
if (align & 1) {
*s++ = '\0';
maxlen--;
}
}
res = smb_encode_path(server, s, maxlen-1, entry, name); res = smb_encode_path(server, s, maxlen-1, entry, name);
if (res < 0) if (res < 0)
return res; return res;
...@@ -908,11 +978,14 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) ...@@ -908,11 +978,14 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
SB_of(server)->s_maxbytes = ~0ULL >> 1; SB_of(server)->s_maxbytes = ~0ULL >> 1;
VERBOSE("LFS enabled\n"); VERBOSE("LFS enabled\n");
} }
#if 0
/* flags we will test for other patches ... */
if (server->opt.capabilities & SMB_CAP_UNICODE) { if (server->opt.capabilities & SMB_CAP_UNICODE) {
server->mnt->flags |= SMB_MOUNT_UNICODE;
VERBOSE("Unicode enabled\n"); VERBOSE("Unicode enabled\n");
} else {
server->mnt->flags &= ~SMB_MOUNT_UNICODE;
} }
#if 0
/* flags we may test for other patches ... */
if (server->opt.capabilities & SMB_CAP_LARGE_READX) { if (server->opt.capabilities & SMB_CAP_LARGE_READX) {
VERBOSE("Large reads enabled\n"); VERBOSE("Large reads enabled\n");
} }
...@@ -997,10 +1070,15 @@ smb_setup_header(struct smb_sb_info * server, __u8 command, __u16 wct, __u16 bcc ...@@ -997,10 +1070,15 @@ smb_setup_header(struct smb_sb_info * server, __u8 command, __u16 wct, __u16 bcc
WSET(buf, smb_uid, server->opt.server_uid); WSET(buf, smb_uid, server->opt.server_uid);
WSET(buf, smb_mid, 1); WSET(buf, smb_mid, 1);
if (server->opt.protocol > SMB_PROTOCOL_CORE) if (server->opt.protocol > SMB_PROTOCOL_CORE) {
{ int flags = SMB_FLAGS_CASELESS_PATHNAMES;
*(buf+smb_flg) = 0x8; int flags2 = SMB_FLAGS2_LONG_PATH_COMPONENTS |
WSET(buf, smb_flg2, 0x3); SMB_FLAGS2_EXTENDED_ATTRIBUTES; /* EA? not really ... */
*(buf+smb_flg) = flags;
if (server->mnt->flags & SMB_MOUNT_UNICODE)
flags2 |= SMB_FLAGS2_UNICODE_STRINGS;
WSET(buf, smb_flg2, flags2);
} }
*p++ = wct; /* wct */ *p++ = wct; /* wct */
p += 2 * wct; p += 2 * wct;
...@@ -1953,6 +2031,7 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level, ...@@ -1953,6 +2031,7 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level,
unsigned int len = 0; unsigned int len = 0;
int n; int n;
__u16 date, time; __u16 date, time;
int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE);
/* /*
* SMB doesn't have a concept of inode numbers ... * SMB doesn't have a concept of inode numbers ...
...@@ -1988,9 +2067,9 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level, ...@@ -1988,9 +2067,9 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level,
result = p + WVAL(p, 0); result = p + WVAL(p, 0);
len = DVAL(p, 60); len = DVAL(p, 60);
if (len > 255) len = 255; if (len > 255) len = 255;
/* NT4 null terminates */ /* NT4 null terminates, unless we are using unicode ... */
qname->name = p + 94; qname->name = p + 94;
if (len && qname->name[len-1] == '\0') if (!unicode && len && qname->name[len-1] == '\0')
len--; len--;
fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 8)); fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 8));
......
...@@ -178,8 +178,8 @@ struct smb_ops { ...@@ -178,8 +178,8 @@ struct smb_ops {
/* --- --- --- end of "static" entries --- --- --- */ /* --- --- --- end of "static" entries --- --- --- */
int (*convert)(char *output, int olen, int (*convert)(unsigned char *output, int olen,
const char *input, int ilen, const unsigned char *input, int ilen,
struct nls_table *nls_from, struct nls_table *nls_from,
struct nls_table *nls_to); struct nls_table *nls_to);
}; };
......
...@@ -37,6 +37,7 @@ struct smb_mount_data { ...@@ -37,6 +37,7 @@ struct smb_mount_data {
#define SMB_MOUNT_OLDATTR 0x0002 /* Use core getattr (Win 95 speedup) */ #define SMB_MOUNT_OLDATTR 0x0002 /* Use core getattr (Win 95 speedup) */
#define SMB_MOUNT_DIRATTR 0x0004 /* Use find_first for getattr */ #define SMB_MOUNT_DIRATTR 0x0004 /* Use find_first for getattr */
#define SMB_MOUNT_CASE 0x0008 /* Be case sensitive */ #define SMB_MOUNT_CASE 0x0008 /* Be case sensitive */
#define SMB_MOUNT_UNICODE 0x0010 /* Server talks unicode */
struct smb_mount_data_kernel { struct smb_mount_data_kernel {
......
...@@ -310,4 +310,22 @@ ...@@ -310,4 +310,22 @@
#define SMB_SET_FILE_ALLOCATION_INFO 0x103 #define SMB_SET_FILE_ALLOCATION_INFO 0x103
#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 #define SMB_SET_FILE_END_OF_FILE_INFO 0x104
/* smb_flg field flags */
#define SMB_FLAGS_SUPPORT_LOCKREAD 0x01
#define SMB_FLAGS_CLIENT_BUF_AVAIL 0x02
#define SMB_FLAGS_RESERVED 0x04
#define SMB_FLAGS_CASELESS_PATHNAMES 0x08
#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10
#define SMB_FLAGS_REQUEST_OPLOCK 0x20
#define SMB_FLAGS_REQUEST_BATCH_OPLOCK 0x40
#define SMB_FLAGS_REPLY 0x80
/* smb_flg2 field flags (samba-2.2.0/source/include/smb.h) */
#define SMB_FLAGS2_LONG_PATH_COMPONENTS 0x0001
#define SMB_FLAGS2_EXTENDED_ATTRIBUTES 0x0002
#define SMB_FLAGS2_DFS_PATHNAMES 0x1000
#define SMB_FLAGS2_READ_PERMIT_NO_EXECUTE 0x2000
#define SMB_FLAGS2_32_BIT_ERROR_CODES 0x4000
#define SMB_FLAGS2_UNICODE_STRINGS 0x8000
#endif /* _SMBNO_H_ */ #endif /* _SMBNO_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment