aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/dir.c
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2013-09-05 08:38:11 -0400
committerSteve French <smfrench@gmail.com>2013-09-08 15:38:08 -0400
commitec71e0e15937ae3d0d8342b564c63649b23afa3a (patch)
tree7cd0cf52ae40771c27ed785c93d26ce316013ef7 /fs/cifs/dir.c
parentc2ccf53dd0ddf0b48e68206c1abb99536851c7b2 (diff)
cifs: convert case-insensitive dentry ops to use new case conversion routines
Have the case-insensitive d_compare and d_hash routines convert each character in the filenames to wchar_t's and then use the new cifs_toupper routine to convert those into uppercase. With this scheme we should more closely emulate the case conversion that the servers will do. Reported-and-Tested-by: Jan-Marek Glogowski <glogow@fbihome.de> Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs/dir.c')
-rw-r--r--fs/cifs/dir.c58
1 files changed, 50 insertions, 8 deletions
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index d62ce0d48141..d3e2eaa503a6 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -32,6 +32,7 @@
32#include "cifsproto.h" 32#include "cifsproto.h"
33#include "cifs_debug.h" 33#include "cifs_debug.h"
34#include "cifs_fs_sb.h" 34#include "cifs_fs_sb.h"
35#include "cifs_unicode.h"
35 36
36static void 37static void
37renew_parental_timestamps(struct dentry *direntry) 38renew_parental_timestamps(struct dentry *direntry)
@@ -834,12 +835,17 @@ static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q)
834{ 835{
835 struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; 836 struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls;
836 unsigned long hash; 837 unsigned long hash;
837 int i; 838 wchar_t c;
839 int i, charlen;
838 840
839 hash = init_name_hash(); 841 hash = init_name_hash();
840 for (i = 0; i < q->len; i++) 842 for (i = 0; i < q->len; i += charlen) {
841 hash = partial_name_hash(nls_tolower(codepage, q->name[i]), 843 charlen = codepage->char2uni(&q->name[i], q->len - i, &c);
842 hash); 844 /* error out if we can't convert the character */
845 if (unlikely(charlen < 0))
846 return charlen;
847 hash = partial_name_hash(cifs_toupper(c), hash);
848 }
843 q->hash = end_name_hash(hash); 849 q->hash = end_name_hash(hash);
844 850
845 return 0; 851 return 0;
@@ -849,11 +855,47 @@ static int cifs_ci_compare(const struct dentry *parent, const struct dentry *den
849 unsigned int len, const char *str, const struct qstr *name) 855 unsigned int len, const char *str, const struct qstr *name)
850{ 856{
851 struct nls_table *codepage = CIFS_SB(parent->d_sb)->local_nls; 857 struct nls_table *codepage = CIFS_SB(parent->d_sb)->local_nls;
858 wchar_t c1, c2;
859 int i, l1, l2;
852 860
853 if ((name->len == len) && 861 /*
854 (nls_strnicmp(codepage, name->name, str, len) == 0)) 862 * We make the assumption here that uppercase characters in the local
855 return 0; 863 * codepage are always the same length as their lowercase counterparts.
856 return 1; 864 *
865 * If that's ever not the case, then this will fail to match it.
866 */
867 if (name->len != len)
868 return 1;
869
870 for (i = 0; i < len; i += l1) {
871 /* Convert characters in both strings to UTF-16. */
872 l1 = codepage->char2uni(&str[i], len - i, &c1);
873 l2 = codepage->char2uni(&name->name[i], name->len - i, &c2);
874
875 /*
876 * If we can't convert either character, just declare it to
877 * be 1 byte long and compare the original byte.
878 */
879 if (unlikely(l1 < 0 && l2 < 0)) {
880 if (str[i] != name->name[i])
881 return 1;
882 l1 = 1;
883 continue;
884 }
885
886 /*
887 * Here, we again ass|u|me that upper/lowercase versions of
888 * a character are the same length in the local NLS.
889 */
890 if (l1 != l2)
891 return 1;
892
893 /* Now compare uppercase versions of these characters */
894 if (cifs_toupper(c1) != cifs_toupper(c2))
895 return 1;
896 }
897
898 return 0;
857} 899}
858 900
859const struct dentry_operations cifs_ci_dentry_ops = { 901const struct dentry_operations cifs_ci_dentry_ops = {