lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 23 Sep 2022 15:02:45 +0300
From:   Konstantin Komarov <almaz.alexandrovich@...agon-software.com>
To:     <ntfs3@...ts.linux.dev>
CC:     LKML <linux-kernel@...r.kernel.org>,
        <linux-fsdevel@...r.kernel.org>
Subject: [PATCH 1/2] fs/ntfs3: Add option "nocase"

This commit adds mount option and additional functions.

Signed-off-by: Konstantin Komarov <almaz.alexandrovich@...agon-software.com>
---
  fs/ntfs3/index.c   |   2 +-
  fs/ntfs3/namei.c   | 139 +++++++++++++++++++++++++++++++++++++++++++++
  fs/ntfs3/ntfs_fs.h |   4 ++
  fs/ntfs3/super.c   |   6 ++
  fs/ntfs3/upcase.c  |  12 ++++
  5 files changed, 162 insertions(+), 1 deletion(-)

diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c
index 440328147e7e..613036f9c6e6 100644
--- a/fs/ntfs3/index.c
+++ b/fs/ntfs3/index.c
@@ -47,7 +47,7 @@ static int cmp_fnames(const void *key1, size_t l1, const void *key2, size_t l2,
  	if (l2 < fsize2)
  		return -1;
  
-	both_case = f2->type != FILE_NAME_DOS /*&& !sbi->options.nocase*/;
+	both_case = f2->type != FILE_NAME_DOS && !sbi->options->nocase;
  	if (!l1) {
  		const struct le_str *s2 = (struct le_str *)&f2->name_len;
  
diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c
index bc22cc321a74..315763eb05ff 100644
--- a/fs/ntfs3/namei.c
+++ b/fs/ntfs3/namei.c
@@ -7,6 +7,7 @@
  
  #include <linux/fs.h>
  #include <linux/nls.h>
+#include <linux/ctype.h>
  
  #include "debug.h"
  #include "ntfs.h"
@@ -355,6 +356,138 @@ struct dentry *ntfs3_get_parent(struct dentry *child)
  	return ERR_PTR(-ENOENT);
  }
  
+/*
+ * dentry_operations::d_hash
+ */
+static int ntfs_d_hash(const struct dentry *dentry, struct qstr *name)
+{
+	struct ntfs_sb_info *sbi;
+	const char *n = name->name;
+	unsigned int len = name->len;
+	unsigned long hash;
+	struct cpu_str *uni;
+	unsigned int c;
+	int err;
+
+	/* First try fast implementation. */
+	hash = init_name_hash(dentry);
+
+	for (;;) {
+		if (!len--) {
+			name->hash = end_name_hash(hash);
+			return 0;
+		}
+
+		c = *n++;
+		if (c >= 0x80)
+			break;
+
+		hash = partial_name_hash(toupper(c), hash);
+	}
+
+	/*
+	 * Try slow way with current upcase table
+	 */
+	uni = __getname();
+	if (!uni)
+		return -ENOMEM;
+
+	sbi = dentry->d_sb->s_fs_info;
+
+	err = ntfs_nls_to_utf16(sbi, name->name, name->len, uni, NTFS_NAME_LEN,
+				UTF16_HOST_ENDIAN);
+	if (err < 0)
+		goto out;
+
+	if (!err) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	hash = ntfs_names_hash(uni->name, uni->len, sbi->upcase,
+			       init_name_hash(dentry));
+	name->hash = end_name_hash(hash);
+	err = 0;
+
+out:
+	__putname(uni);
+	return err;
+}
+
+/*
+ * dentry_operations::d_compare
+ */
+static int ntfs_d_compare(const struct dentry *dentry, unsigned int len1,
+			  const char *str, const struct qstr *name)
+{
+	struct ntfs_sb_info *sbi;
+	int ret;
+	const char *n1 = str;
+	const char *n2 = name->name;
+	unsigned int len2 = name->len;
+	unsigned int lm = min(len1, len2);
+	unsigned char c1, c2;
+	struct cpu_str *uni1, *uni2;
+
+	/* First try fast implementation. */
+	for (;;) {
+		if (!lm--) {
+			ret = len1 == len2 ? 0 : 1;
+			goto out;
+		}
+
+		if ((c1 = *n1++) == (c2 = *n2++))
+			continue;
+
+		if (c1 >= 0x80 || c2 >= 0x80)
+			break;
+
+		if (toupper(c1) != toupper(c2)) {
+			ret = 1;
+			goto out;
+		}
+	}
+
+	/*
+	 * Try slow way with current upcase table
+	 */
+	sbi = dentry->d_sb->s_fs_info;
+	uni1 = __getname();
+	if (!uni1)
+		return -ENOMEM;
+
+	ret = ntfs_nls_to_utf16(sbi, str, len1, uni1, NTFS_NAME_LEN,
+				UTF16_HOST_ENDIAN);
+	if (ret < 0)
+		goto out;
+
+	if (!ret) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	uni2 = Add2Ptr(uni1, 2048);
+
+	ret = ntfs_nls_to_utf16(sbi, name->name, name->len, uni2, NTFS_NAME_LEN,
+				UTF16_HOST_ENDIAN);
+	if (ret < 0)
+		goto out;
+
+	if (!ret) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = !ntfs_cmp_names(uni1->name, uni1->len, uni2->name, uni2->len,
+			      sbi->upcase, false)
+		      ? 0
+		      : 1;
+
+out:
+	__putname(uni1);
+	return ret;
+}
+
  // clang-format off
  const struct inode_operations ntfs_dir_inode_operations = {
  	.lookup		= ntfs_lookup,
@@ -382,4 +515,10 @@ const struct inode_operations ntfs_special_inode_operations = {
  	.get_acl	= ntfs_get_acl,
  	.set_acl	= ntfs_set_acl,
  };
+
+const struct dentry_operations ntfs_dentry_ops = {
+	.d_hash		= ntfs_d_hash,
+	.d_compare	= ntfs_d_compare,
+};
+
  // clang-format on
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index cd680ada50ab..6c1c7ef3b2d6 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -101,6 +101,7 @@ struct ntfs_mount_options {
  	unsigned force : 1; /* RW mount dirty volume. */
  	unsigned noacsrules : 1; /* Exclude acs rules. */
  	unsigned prealloc : 1; /* Preallocate space when file is growing. */
+	unsigned nocase : 1; /* case insensitive. */
  };
  
  /* Special value to unpack and deallocate. */
@@ -721,6 +722,7 @@ struct dentry *ntfs3_get_parent(struct dentry *child);
  
  extern const struct inode_operations ntfs_dir_inode_operations;
  extern const struct inode_operations ntfs_special_inode_operations;
+extern const struct dentry_operations ntfs_dentry_ops;
  
  /* Globals from record.c */
  int mi_get(struct ntfs_sb_info *sbi, CLST rno, struct mft_inode **mi);
@@ -840,6 +842,8 @@ int ntfs_cmp_names(const __le16 *s1, size_t l1, const __le16 *s2, size_t l2,
  		   const u16 *upcase, bool bothcase);
  int ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2,
  		       const u16 *upcase, bool bothcase);
+unsigned long ntfs_names_hash(const u16 *name, size_t len, const u16 *upcase,
+			      unsigned long hash);
  
  /* globals from xattr.c */
  #ifdef CONFIG_NTFS3_FS_POSIX_ACL
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index 87d9eabf9847..d72a27abf1c8 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -253,6 +253,7 @@ enum Opt {
  	Opt_iocharset,
  	Opt_prealloc,
  	Opt_noacsrules,
+	Opt_nocase,
  	Opt_err,
  };
  
@@ -272,6 +273,7 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = {
  	fsparam_flag_no("showmeta",		Opt_showmeta),
  	fsparam_flag_no("prealloc",		Opt_prealloc),
  	fsparam_flag_no("acsrules",		Opt_noacsrules),
+	fsparam_flag_no("nocase",		Opt_nocase),
  	fsparam_string("iocharset",		Opt_iocharset),
  	{}
  };
@@ -383,6 +385,9 @@ static int ntfs_fs_parse_param(struct fs_context *fc,
  	case Opt_noacsrules:
  		opts->noacsrules = result.negated ? 1 : 0;
  		break;
+	case Opt_nocase:
+		opts->nocase = result.negated ? 1 : 0;
+		break;
  	default:
  		/* Should not be here unless we forget add case. */
  		return -EINVAL;
@@ -936,6 +941,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
  	sb->s_export_op = &ntfs_export_ops;
  	sb->s_time_gran = NTFS_TIME_GRAN; // 100 nsec
  	sb->s_xattr = ntfs_xattr_handlers;
+	sb->s_d_op = sbi->options->nocase ? &ntfs_dentry_ops : NULL;
  
  	sbi->options->nls = ntfs_load_nls(sbi->options->nls_name);
  	if (IS_ERR(sbi->options->nls)) {
diff --git a/fs/ntfs3/upcase.c b/fs/ntfs3/upcase.c
index b5e8256fd710..7681eefacb4b 100644
--- a/fs/ntfs3/upcase.c
+++ b/fs/ntfs3/upcase.c
@@ -102,3 +102,15 @@ int ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2,
  	diff2 = l1 - l2;
  	return diff2 ? diff2 : diff1;
  }
+
+/* Helper function for ntfs_d_hash. */
+unsigned long ntfs_names_hash(const u16 *name, size_t len, const u16 *upcase,
+			      unsigned long hash)
+{
+	while (len--) {
+		unsigned int c = upcase_unicode_char(upcase, *name++);
+		hash = partial_name_hash(c, hash);
+	}
+
+	return hash;
+}
-- 
2.37.0

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ