Unverified Commit a3a956c7 authored by Konstantin Komarov's avatar Konstantin Komarov
Browse files

fs/ntfs3: Add option "nocase"



This commit adds mount option and additional functions.

Signed-off-by: default avatarKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
parent ae6b47b5
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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;

+139 −0
Original line number Diff line number Diff line
@@ -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
+4 −0
Original line number Diff line number Diff line
@@ -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
+6 −0
Original line number Diff line number Diff line
@@ -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)) {
+12 −0
Original line number Diff line number Diff line
@@ -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;
}