From patchwork Wed Nov 18 06:42:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 11913895 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 66DCEC56202 for ; Wed, 18 Nov 2020 06:43:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EEB1924698 for ; Wed, 18 Nov 2020 06:43:12 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="AVBnh8v1" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726216AbgKRGmx (ORCPT ); Wed, 18 Nov 2020 01:42:53 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51718 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725794AbgKRGmw (ORCPT ); Wed, 18 Nov 2020 01:42:52 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 65A30C061A4F for ; Tue, 17 Nov 2020 22:42:52 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id v188so871955ybc.19 for ; Tue, 17 Nov 2020 22:42:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=DV09QN89xVJWJy2G2Lk6hdWThRemlVWRM2oh2xdx8V4=; b=AVBnh8v1C3R+KGtEy4i7FdueXXEomxX/n+pWlo3sCw+oytZ2q9hGno6HSM7JbvFxwz OrHKZbOdvixT/eQizY+YzgeKTv0DIhEkVYYhBPU3pPK8sXsDPixDe+iha7Ydrr+JA+tN jI2tRDuaXk0i+4VuBZivUjlwtxNDHwWzZnZKS8JjCW74eyusJOGHWsQZGUkYl8gj14UB prKHhzqZWojkco+Wv9VgqltNqWvBEtpjgoKPbVXhiabvoPRQBIoUXyShIYeTyNps9HLV PvJYtWLReRl8A2jxvzTvygcUveMwL8qxkzhkUktD1hg5glU9IOR39afCciwVwlD3iqYQ A12Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=DV09QN89xVJWJy2G2Lk6hdWThRemlVWRM2oh2xdx8V4=; b=cIhCi785ejCZnAA4i4qXO7PGdiuw7CxiW+69qRk3BFzSd+3MG2MMmUDK4suMDg6tad n2ylX06gQyMzJBL63wNjGGuX1toZn33yRN/uQ4ANX0PkpqGTUD/toIMLjZ2jfeSWGZQA AMVSZgg43nqTeT7LWYNaPY1TcwvAjVW8c5EuZ7rBDmxL2Ge2tvvj809jIcVA9BCEqW7b LWQP6h49/FWs+bP7Fqa2hQci7a8mpQ61JM0l7YNNKrMbzPdFuWVSBuQ+GebfNb28/lOP LLL2VdKI58yMobX4ml8r0+NncHOoNtSvkhx0QgXkj15p6RUlc8F7JoULY0UzIqrY1iDx n5ow== X-Gm-Message-State: AOAM533CyWkZfQNILKad/cEIHli+Ze3BSKt3/KSjNLv931c4HAJEHpsX Snf1T4wjhznXznPvjKwMe7WUMxB+wQA= X-Google-Smtp-Source: ABdhPJxOd7e3q7u9BtHqGyF6Mlg3/h4hF+stEzn8jpxyxNb8EuNiIyfWe5DKbwvldnocmuozJqNRYtSR7EM= Sender: "drosen via sendgmr" X-Received: from drosen.c.googlers.com ([fda3:e722:ac3:10:24:72f4:c0a8:4e6f]) (user=drosen job=sendgmr) by 2002:a25:e7cd:: with SMTP id e196mr4372701ybh.375.1605681771546; Tue, 17 Nov 2020 22:42:51 -0800 (PST) Date: Wed, 18 Nov 2020 06:42:43 +0000 In-Reply-To: <20201118064245.265117-1-drosen@google.com> Message-Id: <20201118064245.265117-2-drosen@google.com> Mime-Version: 1.0 References: <20201118064245.265117-1-drosen@google.com> X-Mailer: git-send-email 2.29.2.299.gdc1121823c-goog Subject: [PATCH v3 1/3] libfs: Add generic function for setting dentry_ops From: Daniel Rosenberg To: "Theodore Y . Ts'o" , Jaegeuk Kim , Eric Biggers , Andreas Dilger , Chao Yu , Alexander Viro , Richard Weinberger , linux-fscrypt@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net Cc: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mtd@lists.infradead.org, Gabriel Krisman Bertazi , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org This adds a function to set dentry operations at lookup time that will work for both encrypted filenames and casefolded filenames. A filesystem that supports both features simultaneously can use this function during lookup preparations to set up its dentry operations once fscrypt no longer does that itself. Currently the casefolding dentry operation are always set if the filesystem defines an encoding because the features is toggleable on empty directories. Unlike in the encryption case, the dentry operations used come from the parent. Since we don't know what set of functions we'll eventually need, and cannot change them later, we enable the casefolding operations if the filesystem supports them at all. By splitting out the various cases, we support as few dentry operations as we can get away with, maximizing compatibility with overlayfs, which will not function if a filesystem supports certain dentry_operations. Signed-off-by: Daniel Rosenberg --- fs/libfs.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 1 + 2 files changed, 71 insertions(+) diff --git a/fs/libfs.c b/fs/libfs.c index fc34361c1489..babef1f7b50e 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1449,4 +1449,74 @@ int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str) return 0; } EXPORT_SYMBOL(generic_ci_d_hash); + +static const struct dentry_operations generic_ci_dentry_ops = { + .d_hash = generic_ci_d_hash, + .d_compare = generic_ci_d_compare, +}; +#endif + +#ifdef CONFIG_FS_ENCRYPTION +static const struct dentry_operations generic_encrypted_dentry_ops = { + .d_revalidate = fscrypt_d_revalidate, +}; +#endif + +#if IS_ENABLED(CONFIG_UNICODE) && IS_ENABLED(CONFIG_FS_ENCRYPTION) +static const struct dentry_operations generic_encrypted_ci_dentry_ops = { + .d_hash = generic_ci_d_hash, + .d_compare = generic_ci_d_compare, + .d_revalidate = fscrypt_d_revalidate, +}; +#endif + +/** + * generic_set_encrypted_ci_d_ops - helper for setting d_ops for given dentry + * @dentry: dentry to set ops on + * + * Casefolded directories need d_hash and d_compare set, so that the dentries + * contained in them are handled case-insensitively. Note that these operations + * are needed on the parent directory rather than on the dentries in it, and + * while the casefolding flag can be toggled on and off on an empty directory, + * dentry_operations can't be changed later. As a result, if the filesystem has + * casefolding support enabled at all, we have to give all dentries the + * casefolding operations even if their inode doesn't have the casefolding flag + * currently (and thus the casefolding ops would be no-ops for now). + * + * Encryption works differently in that the only dentry operation it needs is + * d_revalidate, which it only needs on dentries that have the no-key name flag. + * The no-key flag can't be set "later", so we don't have to worry about that. + * + * Finally, to maximize compatibility with overlayfs (which isn't compatible + * with certain dentry operations) and to avoid taking an unnecessary + * performance hit, we use custom dentry_operations for each possible + * combination rather than always installing all operations. + */ +void generic_set_encrypted_ci_d_ops(struct dentry *dentry) +{ +#ifdef CONFIG_FS_ENCRYPTION + bool needs_encrypt_ops = dentry->d_flags & DCACHE_NOKEY_NAME; +#endif +#ifdef CONFIG_UNICODE + bool needs_ci_ops = dentry->d_sb->s_encoding; +#endif +#if defined(CONFIG_FS_ENCRYPTION) && defined(CONFIG_UNICODE) + if (needs_encrypt_ops && needs_ci_ops) { + d_set_d_op(dentry, &generic_encrypted_ci_dentry_ops); + return; + } #endif +#ifdef CONFIG_FS_ENCRYPTION + if (needs_encrypt_ops) { + d_set_d_op(dentry, &generic_encrypted_dentry_ops); + return; + } +#endif +#ifdef CONFIG_UNICODE + if (needs_ci_ops) { + d_set_d_op(dentry, &generic_ci_dentry_ops); + return; + } +#endif +} +EXPORT_SYMBOL(generic_set_encrypted_ci_d_ops); diff --git a/include/linux/fs.h b/include/linux/fs.h index 8667d0cdc71e..11345e66353b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3202,6 +3202,7 @@ extern int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str); extern int generic_ci_d_compare(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); #endif +extern void generic_set_encrypted_ci_d_ops(struct dentry *dentry); #ifdef CONFIG_MIGRATION extern int buffer_migrate_page(struct address_space *, From patchwork Wed Nov 18 06:42:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 11913899 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EF86CC64E8A for ; Wed, 18 Nov 2020 06:43:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AC7162468F for ; Wed, 18 Nov 2020 06:43:15 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="pXiZqbdj" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726561AbgKRGmy (ORCPT ); Wed, 18 Nov 2020 01:42:54 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51726 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726533AbgKRGmy (ORCPT ); Wed, 18 Nov 2020 01:42:54 -0500 Received: from mail-qv1-xf49.google.com (mail-qv1-xf49.google.com [IPv6:2607:f8b0:4864:20::f49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 21E87C061A4F for ; Tue, 17 Nov 2020 22:42:54 -0800 (PST) Received: by mail-qv1-xf49.google.com with SMTP id x9so652237qvt.16 for ; Tue, 17 Nov 2020 22:42:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=Tb4l37XCxNgRkiCuLXd252PTPoP0yAHbX6esqKI6HxE=; b=pXiZqbdjpI6FS+Nf9Oz84U4XDsvhLq2We74VTam9v7kAlE0+2Xxv1TlYPBzVZZO9DD Jd/2kykrIKxXqiqyKKlcEHXrxkgQmQpQmQmy/e4n+k6my/OuywIvWTasP7fN3aEjMvAv PajpYYF2Lw3l+ZMsWfc5EJGqlzeNJZEqGbcRoHEX1z7gbxW72W+DcFvJ4r1oNEV3jPnE HJ/jehb0QhtLvpaW9QPr8SnfHyIUvDC0FZkgCwVrKj/JDS2MTTZZn25Cq6ZnDqWuDUiR 2XD02y2Pz7Jn93zvoRFTsJjbcGiBjfnw+Q8isem7eovK3mUnMMR/lypa8ORR6FAqvrNU Qxzg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Tb4l37XCxNgRkiCuLXd252PTPoP0yAHbX6esqKI6HxE=; b=e7JeaFS6ri3SValT2ETVTt04BFrxuUCaEsNaFxz1E0u90Y1mlI6fX6SP89O4+kut27 N3UStrFM9bqWT1I69KjI63lgd3qeXZjkJ2WyNMtZRFnH+emd67AYSHViYmcyrcmNMmAf UvWQcvZUH03d3QhMI2hYW7ratHBfPEps2uStHFlyHib0H884Qfm9M2UvmptRUx9fNLBm lpiBdvXgLF8jG3A3iDrYexat0YuhxDxCE168G0Nqs7Y0y91rKknSRw9M6h9v1Fhf4B8q 3hEKguUke8aIzrMaE13hWPoLXfUvG86FY3g7hieLI3U7vmZcZG0pAwiu012jPihyTmCA 6xNQ== X-Gm-Message-State: AOAM530sEtBsuP6DbdsEYDDyc6n+J1o5oY3wOqIw62wK+Ht8NgvDJ+ta Y53CaUNJ+Jdagh/Iru3gtnb/8D3ElMc= X-Google-Smtp-Source: ABdhPJwv3G3ea/OrqQGgB923Wo3qJWcLBr01dzKqbBgRqsKQzZGH5UojLSFhgI5S2N6gk77lAXmR2ZNhFJI= Sender: "drosen via sendgmr" X-Received: from drosen.c.googlers.com ([fda3:e722:ac3:10:24:72f4:c0a8:4e6f]) (user=drosen job=sendgmr) by 2002:ad4:4ae4:: with SMTP id cp4mr3102637qvb.21.1605681773366; Tue, 17 Nov 2020 22:42:53 -0800 (PST) Date: Wed, 18 Nov 2020 06:42:44 +0000 In-Reply-To: <20201118064245.265117-1-drosen@google.com> Message-Id: <20201118064245.265117-3-drosen@google.com> Mime-Version: 1.0 References: <20201118064245.265117-1-drosen@google.com> X-Mailer: git-send-email 2.29.2.299.gdc1121823c-goog Subject: [PATCH v3 2/3] fscrypt: Have filesystems handle their d_ops From: Daniel Rosenberg To: "Theodore Y . Ts'o" , Jaegeuk Kim , Eric Biggers , Andreas Dilger , Chao Yu , Alexander Viro , Richard Weinberger , linux-fscrypt@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net Cc: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mtd@lists.infradead.org, Gabriel Krisman Bertazi , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org This shifts the responsibility of setting up dentry operations from fscrypt to the individual filesystems, allowing them to have their own operations while still setting fscrypt's d_revalidate as appropriate. Most filesystems can just use generic_set_encrypted_ci_d_ops, unless they have their own specific dentry operations as well. That operation will set the minimal d_ops required under the circumstances. Since the fscrypt d_ops are set later on, we must set all d_ops there, since we cannot adjust those later on. This should not result in any change in behavior. Signed-off-by: Daniel Rosenberg --- fs/crypto/fname.c | 4 ---- fs/crypto/hooks.c | 1 - fs/ext4/dir.c | 7 ------- fs/ext4/ext4.h | 4 ---- fs/ext4/namei.c | 1 + fs/ext4/super.c | 5 ----- fs/f2fs/dir.c | 7 ------- fs/f2fs/f2fs.h | 3 --- fs/f2fs/namei.c | 1 + fs/f2fs/super.c | 1 - fs/ubifs/dir.c | 1 + include/linux/fscrypt.h | 7 +++++-- 12 files changed, 8 insertions(+), 34 deletions(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 1fbe6c24d705..cb3cfa6329ba 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -570,7 +570,3 @@ int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) return valid; } EXPORT_SYMBOL_GPL(fscrypt_d_revalidate); - -const struct dentry_operations fscrypt_d_ops = { - .d_revalidate = fscrypt_d_revalidate, -}; diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 20b0df47fe6a..9006fa983335 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -117,7 +117,6 @@ int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry, spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NOKEY_NAME; spin_unlock(&dentry->d_lock); - d_set_d_op(dentry, &fscrypt_d_ops); } return err; } diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index ca50c90adc4c..e757319a4472 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -667,10 +667,3 @@ const struct file_operations ext4_dir_operations = { .open = ext4_dir_open, .release = ext4_release_dir, }; - -#ifdef CONFIG_UNICODE -const struct dentry_operations ext4_dentry_ops = { - .d_hash = generic_ci_d_hash, - .d_compare = generic_ci_d_compare, -}; -#endif diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index bf9429484462..ad77f01d9e20 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -3380,10 +3380,6 @@ static inline void ext4_unlock_group(struct super_block *sb, /* dir.c */ extern const struct file_operations ext4_dir_operations; -#ifdef CONFIG_UNICODE -extern const struct dentry_operations ext4_dentry_ops; -#endif - /* file.c */ extern const struct inode_operations ext4_file_inode_operations; extern const struct file_operations ext4_file_operations; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 33509266f5a0..12a417ff5648 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1614,6 +1614,7 @@ static struct buffer_head *ext4_lookup_entry(struct inode *dir, struct buffer_head *bh; err = ext4_fname_prepare_lookup(dir, dentry, &fname); + generic_set_encrypted_ci_d_ops(dentry); if (err == -ENOENT) return NULL; if (err) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 6633b20224d5..0288bedf46e1 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -4968,11 +4968,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount4; } -#ifdef CONFIG_UNICODE - if (sb->s_encoding) - sb->s_d_op = &ext4_dentry_ops; -#endif - sb->s_root = d_make_root(root); if (!sb->s_root) { ext4_msg(sb, KERN_ERR, "get root dentry failed"); diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 4b9ef8bbfa4a..71fdf5076461 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -1099,10 +1099,3 @@ const struct file_operations f2fs_dir_operations = { .compat_ioctl = f2fs_compat_ioctl, #endif }; - -#ifdef CONFIG_UNICODE -const struct dentry_operations f2fs_dentry_ops = { - .d_hash = generic_ci_d_hash, - .d_compare = generic_ci_d_compare, -}; -#endif diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index cb700d797296..62b4f31d30e2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -3767,9 +3767,6 @@ static inline void f2fs_update_sit_info(struct f2fs_sb_info *sbi) {} #endif extern const struct file_operations f2fs_dir_operations; -#ifdef CONFIG_UNICODE -extern const struct dentry_operations f2fs_dentry_ops; -#endif extern const struct file_operations f2fs_file_operations; extern const struct inode_operations f2fs_file_inode_operations; extern const struct address_space_operations f2fs_dblock_aops; diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 8fa37d1434de..6edb1ab579a1 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -497,6 +497,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, } err = f2fs_prepare_lookup(dir, dentry, &fname); + generic_set_encrypted_ci_d_ops(dentry); if (err == -ENOENT) goto out_splice; if (err) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 00eff2f51807..f51d52591c99 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -3427,7 +3427,6 @@ static int f2fs_setup_casefold(struct f2fs_sb_info *sbi) sbi->sb->s_encoding = encoding; sbi->sb->s_encoding_flags = encoding_flags; - sbi->sb->s_d_op = &f2fs_dentry_ops; } #else if (f2fs_sb_has_casefold(sbi)) { diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index 155521e51ac5..7a920434d741 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -203,6 +203,7 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry, dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino); err = fscrypt_prepare_lookup(dir, dentry, &nm); + generic_set_encrypted_ci_d_ops(dentry); if (err == -ENOENT) return d_splice_alias(NULL, dentry); if (err) diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index a8f7a43f031b..e72f80482671 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -741,8 +741,11 @@ static inline int fscrypt_prepare_rename(struct inode *old_dir, * directory's encryption key is available, then the lookup is assumed to be by * plaintext name; otherwise, it is assumed to be by no-key name. * - * This also installs a custom ->d_revalidate() method which will invalidate the - * dentry if it was created without the key and the key is later added. + * This will set DCACHE_NOKEY_NAME on the dentry if the lookup is by no-key + * name. In this case the filesystem must assign the dentry a dentry_operations + * which contains fscrypt_d_revalidate (or contains a d_revalidate method that + * calls fscrypt_d_revalidate), so that the dentry will be invalidated if the + * directory's encryption key is later added. * * Return: 0 on success; -ENOENT if the directory's key is unavailable but the * filename isn't a valid no-key name, so a negative dentry should be created; From patchwork Wed Nov 18 06:42:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 11913897 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.2 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED,USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F1608C64E75 for ; Wed, 18 Nov 2020 06:43:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B2AB0246AA for ; Wed, 18 Nov 2020 06:43:14 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="pNtN6+9c" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726624AbgKRGm4 (ORCPT ); Wed, 18 Nov 2020 01:42:56 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51744 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726621AbgKRGmz (ORCPT ); Wed, 18 Nov 2020 01:42:55 -0500 Received: from mail-pl1-x649.google.com (mail-pl1-x649.google.com [IPv6:2607:f8b0:4864:20::649]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D0B40C061A51 for ; Tue, 17 Nov 2020 22:42:55 -0800 (PST) Received: by mail-pl1-x649.google.com with SMTP id t13so699562plo.16 for ; Tue, 17 Nov 2020 22:42:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=eIKyJoJ4Gi2fx2f4spCzwy9XjlKRevxwj2k2EH/6CwM=; b=pNtN6+9csjaLC8Uu7+HDO2O/iMhqOcfQENg6e9wakyF8tet/MbuS0J9Hs+NinaMaGn qNfGA1DZF9FFEZ0E3RhhW70fTn5ERPYWtw3O0ze5hzbcxRmeY1ZheJWYVkeLFMEsSdVi zLpFFandWUtLov9SNhkdpCNxy2AMMu2Q/sQUik7d/VoOGXw4LtWmzbwwWM3W8uMhCuE0 HyLfSd8zC+3+3vL61A33N4OP0ldn3+bv47cHGcL82o8c5prAFNirnTvuGf8Gqrp2lAf9 Z+yMmrNXkVeI8G1cz5RFBM9ck4GYX1ODJn8wTfA5fqOV+MNMVdG7uuzoy0dycYpLwPL/ Y66A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=eIKyJoJ4Gi2fx2f4spCzwy9XjlKRevxwj2k2EH/6CwM=; b=JverUa+wISb0gEYjjHJMoqxwgPizo6a7QszXHShYiykpDmpaqrYh3l7/71/gbfQP8R VWCr54+XAgimCF7mtyDHMX6odQzRpc/nJOZEfAMjx0IPDN4lXM42FvK2OKJ/W4zhhijD 33scT9mBI165EjekUjIxRGxz+vqDLw39H3n0hnej2pWfAvwTBpA3K3kTL1nZZwXSPeiP 34srpo0V61WFXm+XKptrevcUyztf8hidExQj1BXk1ULnxWUhPGIoKFC52DwImSo9NSFb vmKCISyPdLznAkGedMPEulpWbGQgyA4BFvoX4v8pLCkgVW+koA5f0dUcWHWxHmvnVVE+ x3lw== X-Gm-Message-State: AOAM533e3g67VQKhW3AEHEcYUKdtyR0/2G19e+a4PxwNmlOn8qKlxz6W htOwkuY8QBUNXDat2OrK6JQ9XJb26cM= X-Google-Smtp-Source: ABdhPJxxWNWt1b8w6O+fwRzNtV210v40fRe5HtIcmjY6IOzyhPIMfv6clHZUbQQMKSkQZTRwM1axc67u7a8= Sender: "drosen via sendgmr" X-Received: from drosen.c.googlers.com ([fda3:e722:ac3:10:24:72f4:c0a8:4e6f]) (user=drosen job=sendgmr) by 2002:a17:90a:e658:: with SMTP id ep24mr2771964pjb.171.1605681775281; Tue, 17 Nov 2020 22:42:55 -0800 (PST) Date: Wed, 18 Nov 2020 06:42:45 +0000 In-Reply-To: <20201118064245.265117-1-drosen@google.com> Message-Id: <20201118064245.265117-4-drosen@google.com> Mime-Version: 1.0 References: <20201118064245.265117-1-drosen@google.com> X-Mailer: git-send-email 2.29.2.299.gdc1121823c-goog Subject: [PATCH v3 3/3] f2fs: Handle casefolding with Encryption From: Daniel Rosenberg To: "Theodore Y . Ts'o" , Jaegeuk Kim , Eric Biggers , Andreas Dilger , Chao Yu , Alexander Viro , Richard Weinberger , linux-fscrypt@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net Cc: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mtd@lists.infradead.org, Gabriel Krisman Bertazi , kernel-team@android.com, Daniel Rosenberg , Eric Biggers Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org Expand f2fs's casefolding support to include encrypted directories. To index casefolded+encrypted directories, we use the SipHash of the casefolded name, keyed by a key derived from the directory's fscrypt master key. This ensures that the dirhash doesn't leak information about the plaintext filenames. Encryption keys are unavailable during roll-forward recovery, so we can't compute the dirhash when recovering a new dentry in an encrypted + casefolded directory. To avoid having to force a checkpoint when a new file is fsync'ed, store the dirhash on-disk appended to i_name. This patch incorporates work by Eric Biggers and Jaegeuk Kim . Co-developed-by: Eric Biggers Signed-off-by: Eric Biggers Signed-off-by: Daniel Rosenberg --- fs/f2fs/dir.c | 98 +++++++++++++++++++++++++++++++++++----------- fs/f2fs/f2fs.h | 8 ++-- fs/f2fs/hash.c | 11 +++++- fs/f2fs/inline.c | 4 ++ fs/f2fs/recovery.c | 12 +++++- fs/f2fs/super.c | 6 --- 6 files changed, 106 insertions(+), 33 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 71fdf5076461..82b58d1f80eb 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -5,6 +5,7 @@ * Copyright (c) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com/ */ +#include #include #include #include @@ -206,30 +207,55 @@ static struct f2fs_dir_entry *find_in_block(struct inode *dir, /* * Test whether a case-insensitive directory entry matches the filename * being searched for. + * + * Returns 1 for a match, 0 for no match, and -errno on an error. */ -static bool f2fs_match_ci_name(const struct inode *dir, const struct qstr *name, +static int f2fs_match_ci_name(const struct inode *dir, const struct qstr *name, const u8 *de_name, u32 de_name_len) { const struct super_block *sb = dir->i_sb; const struct unicode_map *um = sb->s_encoding; + struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len); struct qstr entry = QSTR_INIT(de_name, de_name_len); int res; + if (IS_ENCRYPTED(dir)) { + const struct fscrypt_str encrypted_name = + FSTR_INIT((u8 *)de_name, de_name_len); + + if (WARN_ON_ONCE(!fscrypt_has_encryption_key(dir))) + return -EINVAL; + + decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL); + if (!decrypted_name.name) + return -ENOMEM; + res = fscrypt_fname_disk_to_usr(dir, 0, 0, &encrypted_name, + &decrypted_name); + if (res < 0) + goto out; + entry.name = decrypted_name.name; + entry.len = decrypted_name.len; + } + res = utf8_strncasecmp_folded(um, name, &entry); - if (res < 0) { - /* - * In strict mode, ignore invalid names. In non-strict mode, - * fall back to treating them as opaque byte sequences. - */ - if (sb_has_strict_encoding(sb) || name->len != entry.len) - return false; - return !memcmp(name->name, entry.name, name->len); + /* + * In strict mode, ignore invalid names. In non-strict mode, + * fall back to treating them as opaque byte sequences. + */ + if (res < 0 && !sb_has_strict_encoding(sb)) { + res = name->len == entry.len && + memcmp(name->name, entry.name, name->len) == 0; + } else { + /* utf8_strncasecmp_folded returns 0 on match */ + res = (res == 0); } - return res == 0; +out: + kfree(decrypted_name.name); + return res; } #endif /* CONFIG_UNICODE */ -static inline bool f2fs_match_name(const struct inode *dir, +static inline int f2fs_match_name(const struct inode *dir, const struct f2fs_filename *fname, const u8 *de_name, u32 de_name_len) { @@ -256,6 +282,7 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d, struct f2fs_dir_entry *de; unsigned long bit_pos = 0; int max_len = 0; + int res = 0; if (max_slots) *max_slots = 0; @@ -273,10 +300,15 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d, continue; } - if (de->hash_code == fname->hash && - f2fs_match_name(d->inode, fname, d->filename[bit_pos], - le16_to_cpu(de->name_len))) - goto found; + if (de->hash_code == fname->hash) { + res = f2fs_match_name(d->inode, fname, + d->filename[bit_pos], + le16_to_cpu(de->name_len)); + if (res < 0) + return ERR_PTR(res); + if (res) + goto found; + } if (max_slots && max_len > *max_slots) *max_slots = max_len; @@ -326,7 +358,11 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, } de = find_in_block(dir, dentry_page, fname, &max_slots); - if (de) { + if (IS_ERR(de)) { + *res_page = ERR_CAST(de); + de = NULL; + break; + } else if (de) { *res_page = dentry_page; break; } @@ -448,17 +484,39 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, f2fs_put_page(page, 1); } -static void init_dent_inode(const struct f2fs_filename *fname, +static void init_dent_inode(struct inode *dir, struct inode *inode, + const struct f2fs_filename *fname, struct page *ipage) { struct f2fs_inode *ri; + if (!fname) /* tmpfile case? */ + return; + f2fs_wait_on_page_writeback(ipage, NODE, true, true); /* copy name info. to this inode page */ ri = F2FS_INODE(ipage); ri->i_namelen = cpu_to_le32(fname->disk_name.len); memcpy(ri->i_name, fname->disk_name.name, fname->disk_name.len); + if (IS_ENCRYPTED(dir)) { + file_set_enc_name(inode); + /* + * Roll-forward recovery doesn't have encryption keys available, + * so it can't compute the dirhash for encrypted+casefolded + * filenames. Append it to i_name if possible. Else, disable + * roll-forward recovery of the dentry (i.e., make fsync'ing the + * file force a checkpoint) by setting LOST_PINO. + */ + if (IS_CASEFOLDED(dir)) { + if (fname->disk_name.len + sizeof(f2fs_hash_t) <= + F2FS_NAME_LEN) + put_unaligned(fname->hash, (f2fs_hash_t *) + &ri->i_name[fname->disk_name.len]); + else + file_lost_pino(inode); + } + } set_page_dirty(ipage); } @@ -541,11 +599,7 @@ struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir, return page; } - if (fname) { - init_dent_inode(fname, page); - if (IS_ENCRYPTED(dir)) - file_set_enc_name(inode); - } + init_dent_inode(dir, inode, fname, page); /* * This file should be checkpointed during fsync. diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 62b4f31d30e2..878308736761 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -533,9 +533,11 @@ struct f2fs_filename { #ifdef CONFIG_UNICODE /* * For casefolded directories: the casefolded name, but it's left NULL - * if the original name is not valid Unicode or if the filesystem is - * doing an internal operation where usr_fname is also NULL. In these - * cases we fall back to treating the name as an opaque byte sequence. + * if the original name is not valid Unicode, if the directory is both + * casefolded and encrypted and its encryption key is unavailable, or if + * the filesystem is doing an internal operation where usr_fname is also + * NULL. In all these cases we fall back to treating the name as an + * opaque byte sequence. */ struct fscrypt_str cf_name; #endif diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c index de841aaf3c43..e3beac546c63 100644 --- a/fs/f2fs/hash.c +++ b/fs/f2fs/hash.c @@ -111,7 +111,9 @@ void f2fs_hash_filename(const struct inode *dir, struct f2fs_filename *fname) * If the casefolded name is provided, hash it instead of the * on-disk name. If the casefolded name is *not* provided, that * should only be because the name wasn't valid Unicode, so fall - * back to treating the name as an opaque byte sequence. + * back to treating the name as an opaque byte sequence. Note + * that to handle encrypted directories, the fallback must use + * usr_fname (plaintext) rather than disk_name (ciphertext). */ WARN_ON_ONCE(!fname->usr_fname->name); if (fname->cf_name.name) { @@ -121,6 +123,13 @@ void f2fs_hash_filename(const struct inode *dir, struct f2fs_filename *fname) name = fname->usr_fname->name; len = fname->usr_fname->len; } + if (IS_ENCRYPTED(dir)) { + struct qstr tmp = QSTR_INIT(name, len); + + fname->hash = + cpu_to_le32(fscrypt_fname_siphash(dir, &tmp)); + return; + } } #endif fname->hash = cpu_to_le32(TEA_hash_name(name, len)); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 70384e31788d..92e9852d316a 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -332,6 +332,10 @@ struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir, make_dentry_ptr_inline(dir, &d, inline_dentry); de = f2fs_find_target_dentry(&d, fname, NULL); unlock_page(ipage); + if (IS_ERR(de)) { + *res_page = ERR_CAST(de); + de = NULL; + } if (de) *res_page = ipage; else diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 4f12ade6410a..0947d36af1a8 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -5,6 +5,7 @@ * Copyright (c) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com/ */ +#include #include #include #include "f2fs.h" @@ -128,7 +129,16 @@ static int init_recovered_filename(const struct inode *dir, } /* Compute the hash of the filename */ - if (IS_CASEFOLDED(dir)) { + if (IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir)) { + /* + * In this case the hash isn't computable without the key, so it + * was saved on-disk. + */ + if (fname->disk_name.len + sizeof(f2fs_hash_t) > F2FS_NAME_LEN) + return -EINVAL; + fname->hash = get_unaligned((f2fs_hash_t *) + &raw_inode->i_name[fname->disk_name.len]); + } else if (IS_CASEFOLDED(dir)) { err = f2fs_init_casefolded_name(dir, fname); if (err) return err; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index f51d52591c99..42293b7ceaf2 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -3399,12 +3399,6 @@ static int f2fs_setup_casefold(struct f2fs_sb_info *sbi) struct unicode_map *encoding; __u16 encoding_flags; - if (f2fs_sb_has_encrypt(sbi)) { - f2fs_err(sbi, - "Can't mount with encoding and encryption"); - return -EINVAL; - } - if (f2fs_sb_read_encoding(sbi->raw_super, &encoding_info, &encoding_flags)) { f2fs_err(sbi,