--- linux-2.6.10-rc3-bk14-original/fs/cifs/cifs_unicode.h 2004-10-19 06:55:28.000000000 +0900 +++ linux-2.6.10-rc3-bk14/fs/cifs/cifs_unicode.h 2004-12-22 16:45:40.000000000 +0900 @@ -29,6 +29,8 @@ * The upper/lower functions are based on a table created by mkupr. * This is a compressed table of upper and lower case conversion. * + * Modified By: Han, Donghoon (nazgul33 at nazgul33 dot com) 2004/12/21 + * */ #include @@ -61,6 +63,7 @@ extern struct UniCaseRange UniLowerRange #ifdef __KERNEL__ int cifs_strfromUCS_le(char *, const wchar_t *, int, const struct nls_table *); int cifs_strtoUCS(wchar_t *, const char *, int, const struct nls_table *); +int cifs_strfromUCS_le_fitBufLen(char *, int, const wchar_t *, int, const struct nls_table *); #endif /* --- linux-2.6.10-rc3-bk14-original/fs/cifs/cifs_unicode.c 2004-10-19 06:55:36.000000000 +0900 +++ linux-2.6.10-rc3-bk14/fs/cifs/cifs_unicode.c 2004-12-22 16:45:40.000000000 +0900 @@ -17,6 +17,8 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Modified By: Han, Donghoon (nazgul33 at nazgul33 dot com) 2004/12/21 */ #include #include "cifs_unicode.h" @@ -50,6 +52,7 @@ cifs_strfromUCS_le(char *to, const wchar } } to[outlen] = 0; + return outlen; } @@ -85,3 +88,53 @@ cifs_strtoUCS(wchar_t * to, const char * return i; } +/* + * NAME: cifs_strfromUCS() + * + * FUNCTION: Convert little-endian unicode string to character string + * Converted multibyte string never exceeds to buffer length + * if exceeds, remainder will be represented as '~' + */ +int cifs_strfromUCS_le_fitBufLen( char * to, int to_len, const wchar_t * from, int from_len, const struct nls_table *codepage ) +{ + int i,j; + int outlen = 0; + + to_len--; // reserved for null termination + + for ( i=0; (iuni2char(le16_to_cpu(from[i]), conv_buffer, + NLS_MAX_CHARSET_SIZE); + if ( charlen > 0 ) { + /* test if converted mbChar fits in to buffer */ + if ( ( charlen < to_len - outlen ) || + ((charlen == to_len - outlen) + && ((i+1>=from_len) || from[i+1]==0)) ) { + for(j=0; j=from_len) || from[i+1]==0)) ) { + to[outlen++]='?'; + } else { + to[outlen++]='~'; + break; + } + } + } + to[outlen] = 0; + + return outlen; +} + + --- linux-2.6.10-rc3-bk14-original/fs/cifs/file.c 2004-12-22 18:59:11.661706592 +0900 +++ linux-2.6.10-rc3-bk14/fs/cifs/file.c 2004-12-22 16:45:40.000000000 +0900 @@ -19,6 +19,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Modified By: Han, Donghoon (nazgul33 at nazgul33 dot com) 2004/12/21 */ #include #include @@ -37,8 +40,7 @@ extern int cifs_readdir2(struct file *file, void *direntry, filldir_t filldir); /* BB removeme BB */ -int -cifs_open(struct inode *inode, struct file *file) +int cifs_open(struct inode *inode, struct file *file) { int rc = -EACCES; int xid, oplock; @@ -1800,7 +1802,8 @@ construct_dentry(struct qstr *qstring, s static void reset_resume_key(struct file * dir_file, unsigned char * filename, - unsigned int len,int Unicode,struct nls_table * nls_tab) { + unsigned int len) +{ struct cifsFileInfo *cifsFile; cifsFile = (struct cifsFileInfo *)dir_file->private_data; @@ -1810,24 +1813,24 @@ static void reset_resume_key(struct file kfree(cifsFile->search_resume_name); } - if(Unicode) - len *= 2; + /* for safe null termination */ cifsFile->resume_name_length = len; cifsFile->search_resume_name = - kmalloc(cifsFile->resume_name_length, GFP_KERNEL); + kmalloc(cifsFile->resume_name_length + 2, GFP_KERNEL); if(cifsFile->search_resume_name == NULL) { cERROR(1,("failed new resume key allocate, length %d", cifsFile->resume_name_length)); return; } - if(Unicode) - cifs_strtoUCS((wchar_t *) cifsFile->search_resume_name, - filename, len, nls_tab); - else - memcpy(cifsFile->search_resume_name, filename, - cifsFile->resume_name_length); + + memcpy(cifsFile->search_resume_name, filename, + cifsFile->resume_name_length); + + /* for safe null termination */ + cifsFile->search_resume_name[len] = 0; + cifsFile->search_resume_name[len+1] = 0; cFYI(1,("Reset resume key to: %s with len %d",filename,len)); return; } @@ -1836,13 +1839,16 @@ static void reset_resume_key(struct file static int cifs_filldir(struct qstr *pqstring, FILE_DIRECTORY_INFO * pfindData, - struct file *file, filldir_t filldir, void *direntry) + struct file *file, filldir_t filldir, void *direntry, char * mbFileName ) { struct inode *tmp_inode; struct dentry *tmp_dentry; int object_type,rc; - pqstring->name = pfindData->FileName; + if ( mbFileName == NULL ) + mbFileName = pfindData->FileName; + + pqstring->name = mbFileName; /* pqstring->len is already set by caller */ rc = construct_dentry(pqstring, file, &tmp_inode, &tmp_dentry); @@ -1858,12 +1864,12 @@ cifs_filldir(struct qstr *pqstring, FILE /* new inode created, let us hash it */ insert_inode_hash(tmp_inode); } /* else if inode number changed do we rehash it? */ - rc = filldir(direntry, pfindData->FileName, pqstring->len, file->f_pos, + rc = filldir(direntry, mbFileName, pqstring->len, file->f_pos, tmp_inode->i_ino, object_type); if(rc) { /* due to readdir error we need to recalculate resume key so next readdir will restart on right entry */ - cFYI(1,("Error %d on filldir of %s",rc ,pfindData->FileName)); + cFYI(1,("Error %d on filldir of %s",rc ,mbFileName)); } dput(tmp_dentry); return rc; @@ -1872,14 +1878,17 @@ cifs_filldir(struct qstr *pqstring, FILE static int cifs_filldir_unix(struct qstr *pqstring, FILE_UNIX_INFO * pUnixFindData, struct file *file, - filldir_t filldir, void *direntry) + filldir_t filldir, void *direntry, char * mbFileName ) { struct inode *tmp_inode; struct dentry *tmp_dentry; int object_type, rc; - pqstring->name = pUnixFindData->FileName; - pqstring->len = strnlen(pUnixFindData->FileName, MAX_PATHCONF); + if ( mbFileName == NULL ) + mbFileName = pUnixFindData->FileName; + + pqstring->name = mbFileName; + pqstring->len = strnlen(mbFileName, MAX_PATHCONF); rc = construct_dentry(pqstring, file, &tmp_inode, &tmp_dentry); if((tmp_inode == NULL) || (tmp_dentry == NULL)) { @@ -1895,12 +1904,12 @@ cifs_filldir_unix(struct qstr *pqstring, insert_inode_hash(tmp_inode); } /* else if i_ino has changed should we rehash it? */ unix_fill_in_inode(tmp_inode, pUnixFindData, &object_type); - rc = filldir(direntry, pUnixFindData->FileName, pqstring->len, + rc = filldir(direntry, mbFileName, pqstring->len, file->f_pos, tmp_inode->i_ino, object_type); if(rc) { /* due to readdir error we need to recalculate resume key so next readdir will restart on right entry */ - cFYI(1,("Error %d on filldir of %s",rc ,pUnixFindData->FileName)); + cFYI(1,("Error %d on filldir of %s",rc , mbFileName)); } dput(tmp_dentry); return rc; @@ -2096,73 +2105,89 @@ cifs_readdir(struct file *file, void *di cifsFile->resume_name_length); } for (i = 2; i < count + 2; i++) { + /* unicode conversion buffer */ + char convBuf[MAX_PATHCONF]; + char *pmbFileName = NULL; + if (UnixSearch == FALSE) { __u32 len = le32_to_cpu(pfindData->FileNameLength); + pmbFileName = pfindData->FileName; if (Unicode == TRUE) + { + pmbFileName = convBuf; len = - cifs_strfromUCS_le - (pfindData->FileName, - (wchar_t *) - pfindData->FileName, - len / 2, - cifs_sb->local_nls); + cifs_strfromUCS_le_fitBufLen( + pmbFileName, MAX_PATHCONF, + (wchar_t *) pfindData->FileName, len / 2, + cifs_sb->local_nls); + } qstring.len = len; if (((len != 1) - || (pfindData->FileName[0] != '.')) + || (pmbFileName[0] != '.')) && ((len != 2) - || (pfindData-> - FileName[0] != '.') - || (pfindData-> - FileName[1] != '.'))) { + || (pmbFileName[0] != '.') + || (pmbFileName[1] != '.'))) { if(cifs_filldir(&qstring, pfindData, file, filldir, - direntry)) { + direntry, pmbFileName)) { /* do not end search if kernel not ready to take remaining entries yet */ - reset_resume_key(file, pfindData->FileName,qstring.len, - Unicode, cifs_sb->local_nls); + reset_resume_key(file, pfindData->FileName, + le32_to_cpu(pfindData->FileNameLength)); findParms.EndofSearch = 0; break; } file->f_pos++; } } else { /* UnixSearch */ - pfindDataUnix = - (FILE_UNIX_INFO *) pfindData; - if (Unicode == TRUE) + __u32 len = 0; + pfindDataUnix = (FILE_UNIX_INFO *) pfindData; + pmbFileName = pfindDataUnix->FileName; + /* calculate safe length */ + len = le32_to_cpu(pfindDataUnix->NextEntryOffset) + - (__u32)(sizeof(FILE_UNIX_INFO)-1); + /* trim extra null chars */ + if (Unicode == TRUE) { + __u32 lenTmp = len/2-1; + wchar_t * pwc = (wchar_t*)pfindDataUnix->FileName; + while ( lenTmp>1 && pwc[lenTmp-1] == 0 && pwc[lenTmp] == 0 ) + lenTmp--; + len=lenTmp*2; + } else { + __u32 lenTmp = len-1; + char * pmb = pfindDataUnix->FileName; + while ( lenTmp>1 && pmb[lenTmp-1] == 0 && pmb[lenTmp] == 0 ) + lenTmp--; + len = lenTmp; + } + + if (Unicode == TRUE) { + pmbFileName = convBuf; qstring.len = - cifs_strfromUCS_le - (pfindDataUnix->FileName, - (wchar_t *) - pfindDataUnix->FileName, - MAX_PATHCONF, - cifs_sb->local_nls); - else + cifs_strfromUCS_le_fitBufLen( + pmbFileName, MAX_PATHCONF, + (wchar_t *) pfindDataUnix->FileName, len / 2, + cifs_sb->local_nls); + } else qstring.len = - strnlen(pfindDataUnix-> - FileName, - MAX_PATHCONF); - if (((qstring.len != 1) - || (pfindDataUnix-> - FileName[0] != '.')) - && ((qstring.len != 2) - || (pfindDataUnix-> - FileName[0] != '.') - || (pfindDataUnix-> - FileName[1] != '.'))) { + strnlen(pmbFileName, len); + + if (((qstring.len != 1) || (pmbFileName[0] != '.')) + && ((qstring.len != 2) || (pmbFileName[0] != '.') + || (pmbFileName[1] != '.'))) { if(cifs_filldir_unix(&qstring, pfindDataUnix, file, filldir, - direntry)) { + direntry, + pmbFileName)) { /* do not end search if kernel not ready to take remaining entries yet */ findParms.EndofSearch = 0; - reset_resume_key(file, pfindDataUnix->FileName, - qstring.len,Unicode,cifs_sb->local_nls); + reset_resume_key(file, pfindDataUnix->FileName, len ); break; } file->f_pos++; @@ -2293,77 +2318,88 @@ cifs_readdir(struct file *file, void *di } for (i = 0; i < count; i++) { - __u32 len = le32_to_cpu(pfindData-> - FileNameLength); + /* unicode conversion buffer */ + char convBuf[MAX_PATHCONF]; + char *pmbFileName = NULL; + if (UnixSearch == FALSE) { + __u32 len = le32_to_cpu(pfindData->FileNameLength); + pmbFileName = pfindData->FileName; if (Unicode == TRUE) + { + pmbFileName = convBuf; len = - cifs_strfromUCS_le - (pfindData->FileName, - (wchar_t *) - pfindData->FileName, - len / 2, - cifs_sb->local_nls); + cifs_strfromUCS_le_fitBufLen( + pmbFileName, MAX_PATHCONF, + (wchar_t *)pfindData->FileName, len / 2, + cifs_sb->local_nls); + } qstring.len = len; - if (((len != 1) - || (pfindData->FileName[0] != '.')) + if (((len != 1) || (pmbFileName[0] != '.')) && ((len != 2) - || (pfindData->FileName[0] != '.') - || (pfindData->FileName[1] != - '.'))) { + || (pmbFileName[0] != '.') + || (pmbFileName[1] != '.'))) { if(cifs_filldir (&qstring, pfindData, file, filldir, - direntry)) { + direntry, pmbFileName)) { /* do not end search if kernel not ready to take remaining entries yet */ findNextParms.EndofSearch = 0; - reset_resume_key(file, pfindData->FileName,qstring.len, - Unicode,cifs_sb->local_nls); + reset_resume_key(file, pfindData->FileName, + le32_to_cpu(pfindData->FileNameLength)); break; } file->f_pos++; } } else { /* UnixSearch */ - pfindDataUnix = - (FILE_UNIX_INFO *) - pfindData; - if (Unicode == TRUE) + __u32 len = 0; + pfindDataUnix = (FILE_UNIX_INFO *) pfindData; + pmbFileName = pfindDataUnix->FileName; + /* calculate safe length */ + len = le32_to_cpu(pfindDataUnix->NextEntryOffset) + - (__u32)(sizeof(FILE_UNIX_INFO)-1); + /* trim extra null chars */ + if (Unicode == TRUE) { + __u32 lenTmp = len/2-1; + wchar_t * pwc = (wchar_t*)pfindDataUnix->FileName; + while ( lenTmp>1 && pwc[lenTmp-1] == 0 && pwc[lenTmp] == 0 ) + lenTmp--; + len=lenTmp*2; + } else { + __u32 lenTmp = len-1; + char * pmb = pfindDataUnix->FileName; + while ( lenTmp>1 && pmb[lenTmp-1] == 0 && pmb[lenTmp] == 0 ) + lenTmp--; + len = lenTmp; + } + + if (Unicode == TRUE) { + pmbFileName = convBuf; qstring.len = - cifs_strfromUCS_le - (pfindDataUnix->FileName, - (wchar_t *) - pfindDataUnix->FileName, - MAX_PATHCONF, - cifs_sb->local_nls); - else + cifs_strfromUCS_le_fitBufLen( + pmbFileName, MAX_PATHCONF, + (wchar_t *)pfindDataUnix->FileName, len / 2, + cifs_sb->local_nls); + } else qstring.len = - strnlen - (pfindDataUnix-> - FileName, - MAX_PATHCONF); - if (((qstring.len != 1) - || (pfindDataUnix-> - FileName[0] != '.')) - && ((qstring.len != 2) - || (pfindDataUnix-> - FileName[0] != '.') - || (pfindDataUnix-> - FileName[1] != - '.'))) { + strnlen(pmbFileName, len); + + if (((qstring.len != 1) || (pmbFileName[0] != '.')) + && ((qstring.len != 2) || (pmbFileName[0] != '.') + || (pmbFileName[1] != '.'))) { if(cifs_filldir_unix (&qstring, pfindDataUnix, file, filldir, - direntry)) { + direntry, pmbFileName)) { /* do not end search if kernel not ready to take remaining entries yet */ findNextParms.EndofSearch = 0; - reset_resume_key(file, pfindDataUnix->FileName,qstring.len, - Unicode,cifs_sb->local_nls); + reset_resume_key(file, pfindDataUnix->FileName, len ); break; } file->f_pos++;