From patchwork Tue Jul 3 17:06:58 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gabriel Krisman Bertazi X-Patchwork-Id: 938829 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-ext4-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=collabora.co.uk Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 41KrDP3JP2z9s3C for ; Wed, 4 Jul 2018 03:08:21 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934302AbeGCRIT (ORCPT ); Tue, 3 Jul 2018 13:08:19 -0400 Received: from bhuna.collabora.co.uk ([46.235.227.227]:33448 "EHLO bhuna.collabora.co.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934295AbeGCRIR (ORCPT ); Tue, 3 Jul 2018 13:08:17 -0400 Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: krisman) with ESMTPSA id 6FAD9289317 From: Gabriel Krisman Bertazi To: tytso@mit.edu Cc: linux-ext4@vger.kernel.org, darrick.wong@oracle.com, kernel@collabora.com, Gabriel Krisman Bertazi Subject: [PATCH 18/20] ext4: Support encoding-aware file name lookups Date: Tue, 3 Jul 2018 13:06:58 -0400 Message-Id: <20180703170700.9306-19-krisman@collabora.co.uk> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180703170700.9306-1-krisman@collabora.co.uk> References: <20180703170700.9306-1-krisman@collabora.co.uk> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org Signed-off-by: Gabriel Krisman Bertazi --- fs/ext4/namei.c | 60 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 2a4c25c4681d..e4906bac7279 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "ext4.h" #include "ext4_jbd2.h" @@ -1251,15 +1252,26 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block) dx_set_count(entries, count + 1); } +static inline bool ext4_charset_match_name(struct nls_table *charset, + const struct fscrypt_name *fname, + const u8 *de_name, + u32 de_name_len) +{ + return !nls_strncmp(charset, (char *) de_name, de_name_len, + fname->disk_name.name, fname->disk_name.len); +} + /* * Test whether a directory entry matches the filename being searched for. * * Return: %true if the directory entry matches, otherwise %false. */ -static inline bool ext4_match(const struct ext4_filename *fname, +static inline bool ext4_match(const struct inode *parent, + const struct ext4_filename *fname, const struct ext4_dir_entry_2 *de) { struct fscrypt_name f; + const struct ext4_sb_info *sbi = EXT4_SB(parent->i_sb); if (!de->inode) return false; @@ -1269,6 +1281,11 @@ static inline bool ext4_match(const struct ext4_filename *fname, #ifdef CONFIG_EXT4_FS_ENCRYPTION f.crypto_buf = fname->crypto_buf; #endif + + if (sbi->encoding) + return ext4_charset_match_name(sbi->encoding, &f, + de->name, de->name_len); + return fscrypt_match_name(&f, de->name, de->name_len); } @@ -1289,7 +1306,7 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size, /* this code is executed quadratically often */ /* do minimal checking `by hand' */ if ((char *) de + de->name_len <= dlimit && - ext4_match(fname, de)) { + ext4_match(dir, fname, de)) { /* found a match - just to be sure, do * a full check */ if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data, @@ -1345,6 +1362,7 @@ static struct buffer_head * ext4_find_entry (struct inode *dir, struct buffer_head *bh_use[NAMEI_RA_SIZE]; struct buffer_head *bh, *ret = NULL; ext4_lblk_t start, block; + struct ext4_sb_info *sbi = EXT4_SB(dir->i_sb); const u8 *name = d_name->name; size_t ra_max = 0; /* Number of bh's in the readahead buffer, bh_use[] */ @@ -1393,9 +1411,16 @@ static struct buffer_head * ext4_find_entry (struct inode *dir, * On success, or if the error was file not found, * return. Otherwise, fall back to doing a search the * old fashioned way. + * + * Even if we are doing encodings, an exact-match lookup + * could still benefit from DX, so we don't skip it + * entirely. Only if it fails to find a match, we + * fallback to linear search. */ - if (!IS_ERR(ret) || PTR_ERR(ret) != ERR_BAD_DX_DIR) + if ((ret && (!IS_ERR(ret) || PTR_ERR(ret) != ERR_BAD_DX_DIR)) + || (!ret && !sbi->encoding)) goto cleanup_and_exit; + dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, " "falling back\n")); } @@ -1544,6 +1569,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi struct inode *inode; struct ext4_dir_entry_2 *de; struct buffer_head *bh; + struct ext4_sb_info *sbi = EXT4_SB(dir->i_sb); int err; err = fscrypt_prepare_lookup(dir, dentry, flags); @@ -1585,6 +1611,32 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi iput(inode); return ERR_PTR(-EPERM); } + + if (sbi->encoding) { + struct dentry *new; + struct qstr ciname; + char *name; + + name = kmalloc((sizeof(char) * de->name_len) + 1, + GFP_NOFS); + if (!name) + return ERR_PTR(-ENOMEM); + + memcpy(name, de->name, de->name_len); + name[de->name_len] = '\0'; + ciname.len = de->name_len; + ciname.name = name; + new = d_add_ci(dentry, inode, &ciname); + kfree(name); + return new; + } + } else if (sbi->encoding) { + /* Eventually we want to call d_add_ci(dentry, NULL) for + * negative dentries in the encoding case as well. For + * now, prevent the negative dentry from being + * cached. + */ + return NULL; } return d_splice_alias(inode, dentry); } @@ -1796,7 +1848,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, if (ext4_check_dir_entry(dir, NULL, de, bh, buf, buf_size, offset)) return -EFSCORRUPTED; - if (ext4_match(fname, de)) + if (ext4_match(dir, fname, de)) return -EEXIST; nlen = EXT4_DIR_REC_LEN(de->name_len); rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);