lkml.org 
[lkml]   [2002]   [Mar]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH] smbfs codepage fixes for 2.4.18

Ok, I think I've got something regarding the 2.4.18 oopses. (oopsen?)

There are two errors in the changes to smbfs in 2.4.18-rc3 (and 2.5.5)
1. SMB_MAXNAMELEN (max length of a single path component) was used where
SMB_MAXPATHLEN (max total path length) should have been used.
2. The charset conversion routine was modified to return errors as
negative values but not all callers was changed to handle this. When an
"illegal" character was hit the length of the string was set to
0xffffffff and when computing the hash value it read outside the kernel
memory.

Attached is a patch vs 2.4.18 that fixes these issues for me. Please test
and let me know.

If I select a codepage/charset combination that doesn't match I now get a
somewhat cryptic message instead of an oops (just a temporary thing).
"smbfs: filename charset conversion failed"

The file is then hidden, which is bad. Conversion errors should map to '?'
as they used to or do some translation into ":####" strings. I'll do
something about that.


Some comments on what some of you have been doing:

The smbfs remote codepage can never be utf8 since there are no smb servers
that talk utf8. It can be one of the dos codepages, it can be blank or
with additional patches it can be a 2 byte little endian unicode format.

Furthermore, the local charset must be one that matches the chars used in
the remote set. Otherwise you get conversion errors. A few known good
combinations are:

cp850 <-> iso8859-1
cp866 <-> koi8-r
cp932 <-> euc-jp
(the right is the local = linux side)

See also the smb.conf manpage.

But even with these it seems to be possible to create chars that do not
match, and I think it is caused by windows trying to map unicode to a
codepage and not finding a matching char to use.

Local utf8 always matches the remote and is preferred if your system is
setup to handle it.

I would explain the reported
smb_proc_readdir_long: name=<directory> result=-2, rcls=1, err=2
as a name conversion problem. If the conversion failed one way it used to
be truncated and would then fail when sent back to the server. The error
is ERRDOS - ERRbadfile (File not found).

Check the config and the nls maps used.

/Urban
diff -urN -X exclude linux-2.4.18-orig/fs/smbfs/cache.c linux-2.4.18-smbfs/fs/smbfs/cache.c
--- linux-2.4.18-orig/fs/smbfs/cache.c Sat Jan 12 16:55:58 2002
+++ linux-2.4.18-smbfs/fs/smbfs/cache.c Fri Mar 1 21:46:36 2002
@@ -84,7 +84,7 @@
struct list_head *next;

if (d_validate(dent, parent)) {
- if (dent->d_name.len <= SMB_MAXPATHLEN &&
+ if (dent->d_name.len <= SMB_MAXNAMELEN &&
(unsigned long)dent->d_fsdata == fpos) {
if (!dent->d_inode) {
dput(dent);
diff -urN -X exclude linux-2.4.18-orig/fs/smbfs/proc.c linux-2.4.18-smbfs/fs/smbfs/proc.c
--- linux-2.4.18-orig/fs/smbfs/proc.c Fri Mar 1 20:23:38 2002
+++ linux-2.4.18-smbfs/fs/smbfs/proc.c Sat Mar 2 00:04:22 2002
@@ -119,11 +119,6 @@
int n;
wchar_t ch;

- if (!nls_from || !nls_to) {
- PARANOIA("nls_from=%p, nls_to=%p\n", nls_from, nls_to);
- return convert_memcpy(output, olen, input, ilen, NULL, NULL);
- }
-
while (ilen > 0) {
/* convert by changing to unicode and back to the new cp */
n = nls_from->char2uni((unsigned char *)input, ilen, &ch);
@@ -141,6 +136,10 @@
len += n;
}
return len;
+
+ /* FIXME: these error returns will simply make the files disappear
+ if there is a codepage error. uni_xlate? Or treat different errors
+ differently? */
fail:
return n;
}
@@ -226,8 +225,8 @@
if (maxlen < 2)
return -ENAMETOOLONG;

- if (maxlen > SMB_MAXNAMELEN + 1)
- maxlen = SMB_MAXNAMELEN + 1;
+ if (maxlen > SMB_MAXPATHLEN + 1)
+ maxlen = SMB_MAXPATHLEN + 1;

if (entry == NULL)
goto test_name_and_out;
@@ -1579,12 +1578,16 @@
}
#endif

- qname->len = server->convert(server->name_buf, SMB_MAXNAMELEN,
- qname->name, len,
- server->remote_nls, server->local_nls);
- qname->name = server->name_buf;
+ qname->len = 0;
+ len = server->convert(server->name_buf, SMB_MAXNAMELEN,
+ qname->name, len,
+ server->remote_nls, server->local_nls);
+ if (len > 0) {
+ qname->len = len;
+ qname->name = server->name_buf;
+ DEBUG1("len=%d, name=%.*s\n",qname->len,qname->len,qname->name);
+ }

- DEBUG1("len=%d, name=%.*s\n", qname->len, qname->len, qname->name);
return p + 22;
}

@@ -1700,6 +1703,10 @@
for (i = 0; i < count; i++) {
p = smb_decode_short_dirent(server, p,
&qname, &fattr);
+ if (qname.len == 0) {
+ printk(KERN_ERR "smbfs: filename charset conversion failed\n");
+ continue;
+ }

if (entries_seen == 2 && qname.name[0] == '.') {
if (qname.len == 1)
@@ -1737,6 +1744,7 @@
{
char *result;
unsigned int len = 0;
+ int n;
__u16 date, time;

/*
@@ -1812,10 +1820,14 @@
}
#endif

- qname->len = server->convert(server->name_buf, SMB_MAXNAMELEN,
- qname->name, len,
- server->remote_nls, server->local_nls);
- qname->name = server->name_buf;
+ qname->len = 0;
+ n = server->convert(server->name_buf, SMB_MAXNAMELEN,
+ qname->name, len,
+ server->remote_nls, server->local_nls);
+ if (n > 0) {
+ qname->len = n;
+ qname->name = server->name_buf;
+ }

out:
return result;
@@ -1881,7 +1893,7 @@
*/
mask = param + 12;

- mask_len = smb_encode_path(server, mask, SMB_MAXNAMELEN+1, dir, &star);
+ mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star);
if (mask_len < 0) {
result = mask_len;
goto unlock_return;
@@ -2030,6 +2042,10 @@

p = smb_decode_long_dirent(server, p, info_level,
&qname, &fattr);
+ if (qname.len == 0) {
+ printk(KERN_ERR "smbfs: filename charset conversion failed\n");
+ continue;
+ }

/* ignore . and .. from the server */
if (entries_seen == 2 && qname.name[0] == '.') {
@@ -2088,7 +2104,7 @@
int mask_len, result;

retry:
- mask_len = smb_encode_path(server, mask, SMB_MAXNAMELEN+1, dentry, NULL);
+ mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dentry, NULL);
if (mask_len < 0) {
result = mask_len;
goto out;
@@ -2214,7 +2230,7 @@
retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
DSET(param, 2, 0);
- result = smb_encode_path(server, param+6, SMB_MAXNAMELEN+1, dir, NULL);
+ result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL);
if (result < 0)
goto out;
p = param + 6 + result;
@@ -2464,7 +2480,7 @@
retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
DSET(param, 2, 0);
- result = smb_encode_path(server, param+6, SMB_MAXNAMELEN+1, dir, NULL);
+ result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL);
if (result < 0)
goto out;
p = param + 6 + result;
\
 
 \ /
  Last update: 2005-03-22 13:24    [W:0.053 / U:1.308 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site