Commit dbab80e2 authored by Atte Heikkilä's avatar Atte Heikkilä Committed by Steve French
Browse files

ksmbd: make utf-8 file name comparison work in __caseless_lookup()



Case-insensitive file name lookups with __caseless_lookup() use
strncasecmp() for file name comparison. strncasecmp() assumes an
ISO8859-1-compatible encoding, which is not the case here as UTF-8
is always used. As such, use of strncasecmp() here produces correct
results only if both strings use characters in the ASCII range only.
Fix this by using utf8_strncasecmp() if CONFIG_UNICODE is set. On
failure or if CONFIG_UNICODE is not set, fallback to strncasecmp().
Also, as we are adding an include for `linux/unicode.h', include it
in `fs/ksmbd/connection.h' as well since it should be explicit there.

Signed-off-by: default avatarAtte Heikkilä <atteh.mailbox@gmail.com>
Acked-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 7c88c1e0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <net/request_sock.h>
#include <linux/kthread.h>
#include <linux/nls.h>
#include <linux/unicode.h>

#include "smb_common.h"
#include "ksmbd_work.h"
+17 −3
Original line number Diff line number Diff line
@@ -1145,12 +1145,23 @@ static int __caseless_lookup(struct dir_context *ctx, const char *name,
			     unsigned int d_type)
{
	struct ksmbd_readdir_data *buf;
	int cmp = -EINVAL;

	buf = container_of(ctx, struct ksmbd_readdir_data, ctx);

	if (buf->used != namlen)
		return 0;
	if (!strncasecmp((char *)buf->private, name, namlen)) {
	if (IS_ENABLED(CONFIG_UNICODE) && buf->um) {
		const struct qstr q_buf = {.name = buf->private,
					   .len = buf->used};
		const struct qstr q_name = {.name = name,
					    .len = namlen};

		cmp = utf8_strncasecmp(buf->um, &q_buf, &q_name);
	}
	if (cmp < 0)
		cmp = strncasecmp((char *)buf->private, name, namlen);
	if (!cmp) {
		memcpy((char *)buf->private, name, namlen);
		buf->dirent_count = 1;
		return -EEXIST;
@@ -1166,7 +1177,8 @@ static int __caseless_lookup(struct dir_context *ctx, const char *name,
 *
 * Return:	0 on success, otherwise error
 */
static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, size_t namelen)
static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
				   size_t namelen, struct unicode_map *um)
{
	int ret;
	struct file *dfilp;
@@ -1176,6 +1188,7 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, size_t na
		.private	= name,
		.used		= namelen,
		.dirent_count	= 0,
		.um		= um,
	};

	dfilp = dentry_open(dir, flags, current_cred());
@@ -1238,7 +1251,8 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
				break;

			err = ksmbd_vfs_lookup_in_dir(&parent, filename,
						      filename_len);
						      filename_len,
						      work->conn->um);
			path_put(&parent);
			if (err)
				goto out;
+2 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include <linux/namei.h>
#include <uapi/linux/xattr.h>
#include <linux/posix_acl.h>
#include <linux/unicode.h>

#include "smbacl.h"
#include "xattr.h"
@@ -60,6 +61,7 @@ struct ksmbd_readdir_data {
	unsigned int		used;
	unsigned int		dirent_count;
	unsigned int		file_attr;
	struct unicode_map	*um;
};

/* ksmbd kstat wrapper to get valid create time when reading dir entry */