Message ID | 20230313123310.13040-2-lhenriques@suse.de |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:5915:0:0:0:0:0 with SMTP id v21csp1157505wrd; Mon, 13 Mar 2023 05:35:43 -0700 (PDT) X-Google-Smtp-Source: AK7set9isduL7V3UwmtMq33FHCPeC+BWjBN6fNsnaef5lYWQxOid30relsxscj5Dus8Oinga1rMR X-Received: by 2002:a17:902:d2ca:b0:19f:8bbf:9c56 with SMTP id n10-20020a170902d2ca00b0019f8bbf9c56mr6204514plc.3.1678710943401; Mon, 13 Mar 2023 05:35:43 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1678710943; cv=none; d=google.com; s=arc-20160816; b=NMQ5W+t6vyoOr+JnJPzMBIf/58EfFixZbEctUMDX6/S78C5xOFWVSoyS+JiWeRb8vQ 3kIbDvlFl05+frr2Z7PpyzKv2Mas9bIGy2TmiwF0jxDr5p7PM5K1C4yTv+S3pfCexv5L /X2uJjgk3/xa7iBwqCD4ZRXft+XVe8NcR4+6gsh8o3ZuZwjJdtxTTsEmQd500ovfWhoF Kb424dtf0TVslfPuvVneFDfQd9KDsjljIbieXoKoupJnLDISFOja7N3JUY7oVaoeDudr bLVfwwyM3i9mOOuG0Y4PCNYChVCk7n1qvHv6+6Tbk3bFP574KaGLxt9gWaZ+vv679Qu+ txxA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature:dkim-signature; bh=bc2iGS3vObLzmQ9MIUXqwa39aRJrh3amfgSHRWBj7bQ=; b=RbI7DbnZ97L1aI6kdbJoVm8UNZSPgnY5A52UusLaL5o8OPnzpAmElS6Ic1/FyQD5UD mQr0U6R/YupWsaj+S+EiKjwQCUVqY6VhHT5vJgvf4mU/4k33L0seBATxsrqzgoRVWJ21 AL7K8D+0LrRsRadCxKI5T3/ZWTcWiuEcFuPHTFnibgiRSumWrFpEQm4oeOoGlbZuwmu7 aVhZFlBV4+4AqQbcl12FV8w73SdV35Mp/uHBiSquK1da2hZiuIAz1mtiiM+gJ/oZoquq NKnmKX7aO2D+JGiiHfc5bqWIocB7CRc/kZV7myxI92PJvx5N7vCvEbsJib7RTNPqZC5X iMdg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@suse.de header.s=susede2_rsa header.b=HBhJiQau; dkim=neutral (no key) header.i=@suse.de header.b=eBB7qNSi; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=suse.de Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id li12-20020a170903294c00b00189891763f3si218591plb.600.2023.03.13.05.35.28; Mon, 13 Mar 2023 05:35:43 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@suse.de header.s=susede2_rsa header.b=HBhJiQau; dkim=neutral (no key) header.i=@suse.de header.b=eBB7qNSi; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=suse.de Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229796AbjCMMd1 (ORCPT <rfc822;realc9580@gmail.com> + 99 others); Mon, 13 Mar 2023 08:33:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58628 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229734AbjCMMdR (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Mon, 13 Mar 2023 08:33:17 -0400 Received: from smtp-out2.suse.de (smtp-out2.suse.de [IPv6:2001:67c:2178:6::1d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A5B091969F; Mon, 13 Mar 2023 05:33:14 -0700 (PDT) Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 78D4C1FE0C; Mon, 13 Mar 2023 12:33:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1678710793; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=bc2iGS3vObLzmQ9MIUXqwa39aRJrh3amfgSHRWBj7bQ=; b=HBhJiQauaNSAKXRLDJb4C7AN5y6XV0PAD4EEEx2JLMUMBp+an4mWFkMLojnQVNSmxJgMJh aGaV6v48u3t1lZdzBwVD68G9CsNELpiOe+bULzVR4nuT0u7cs+IrPAe+hevLfRl81pOIgC VYJqVLTp2Ef34aSqeSAvnznhZBuaGf4= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1678710793; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=bc2iGS3vObLzmQ9MIUXqwa39aRJrh3amfgSHRWBj7bQ=; b=eBB7qNSiDz9RNbMBX1zyQcYZ0ITMLS1WEE/w/sPjr+8d47mKjBdyHcIu2ZIrv/mqYecZOW Tme07aX8VkAPeiCw== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id CBB8F13582; Mon, 13 Mar 2023 12:33:12 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id 4LfeLggYD2RnCQAAMHmgww (envelope-from <lhenriques@suse.de>); Mon, 13 Mar 2023 12:33:12 +0000 Received: from localhost (brahms.olymp [local]) by brahms.olymp (OpenSMTPD) with ESMTPA id 0119dc8c; Mon, 13 Mar 2023 12:33:11 +0000 (UTC) From: =?utf-8?q?Lu=C3=ADs_Henriques?= <lhenriques@suse.de> To: Eric Biggers <ebiggers@kernel.org>, Xiubo Li <xiubli@redhat.com>, Jeff Layton <jlayton@kernel.org> Cc: "Theodore Y. Ts'o" <tytso@mit.edu>, Jaegeuk Kim <jaegeuk@kernel.org>, Ilya Dryomov <idryomov@gmail.com>, linux-fscrypt@vger.kernel.org, ceph-devel@vger.kernel.org, linux-kernel@vger.kernel.org, =?utf-8?q?Lu?= =?utf-8?q?=C3=ADs_Henriques?= <lhenriques@suse.de> Subject: [PATCH 1/2] fscrypt: new helper function - fscrypt_prepare_atomic_open() Date: Mon, 13 Mar 2023 12:33:09 +0000 Message-Id: <20230313123310.13040-2-lhenriques@suse.de> In-Reply-To: <20230313123310.13040-1-lhenriques@suse.de> References: <20230313123310.13040-1-lhenriques@suse.de> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_MED,SPF_HELO_NONE, SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1760256006226294082?= X-GMAIL-MSGID: =?utf-8?q?1760256006226294082?= |
Series |
ceph: fscrypt: fix atomic open bug for encrypted directories
|
|
Commit Message
Luis Henriques
March 13, 2023, 12:33 p.m. UTC
This patch introduces a new helper function which prepares an atomic_open.
Because atomic open can act as a lookup if handed a dentry that is negative,
we need to set DCACHE_NOKEY_NAME if the key for the parent isn't available.
The reason for getting the encryption info before checking if the directory
has the encryption key is because we may have the key available but the
encryption info isn't yet set (maybe due to a drop_caches). The regular
open path will use fscrypt_file_open for that but in the atomic open a
different approach is required.
Signed-off-by: Luís Henriques <lhenriques@suse.de>
---
fs/crypto/hooks.c | 35 +++++++++++++++++++++++++++++++++++
include/linux/fscrypt.h | 7 +++++++
2 files changed, 42 insertions(+)
Comments
On Mon, Mar 13, 2023 at 12:33:09PM +0000, Luís Henriques wrote: > + * The regular open path will use fscrypt_file_open for that, but in the > + * atomic open a different approach is required. This should actually be fscrypt_prepare_lookup, not fscrypt_file_open, right? > +int fscrypt_prepare_atomic_open(struct inode *dir, struct dentry *dentry) > +{ > + int err; > + > + if (!IS_ENCRYPTED(dir)) > + return 0; > + > + err = fscrypt_get_encryption_info(dir, true); > + if (!err && !fscrypt_has_encryption_key(dir)) { > + spin_lock(&dentry->d_lock); > + dentry->d_flags |= DCACHE_NOKEY_NAME; > + spin_unlock(&dentry->d_lock); > + } > + > + return err; > +} > +EXPORT_SYMBOL_GPL(fscrypt_prepare_atomic_open); [...] > +static inline int fscrypt_prepare_atomic_open(struct inode *dir, > + struct dentry *dentry) > +{ > + return -EOPNOTSUPP; > +} This has different behavior on unencrypted directories depending on whether CONFIG_FS_ENCRYPTION is enabled or not. That's bad. In patch 2, the caller you are introducing has already checked IS_ENCRYPTED(). Also, your kerneldoc comment for fscrypt_prepare_atomic_open() says it is for *encrypted* directories. So IMO, just remove the IS_ENCRYPTED() check from the CONFIG_FS_ENCRYPTION version of fscrypt_prepare_atomic_open(). - Eric
On 14/03/2023 02:09, Eric Biggers wrote: > On Mon, Mar 13, 2023 at 12:33:09PM +0000, Luís Henriques wrote: >> + * The regular open path will use fscrypt_file_open for that, but in the >> + * atomic open a different approach is required. > This should actually be fscrypt_prepare_lookup, not fscrypt_file_open, right? > >> +int fscrypt_prepare_atomic_open(struct inode *dir, struct dentry *dentry) >> +{ >> + int err; >> + >> + if (!IS_ENCRYPTED(dir)) >> + return 0; >> + >> + err = fscrypt_get_encryption_info(dir, true); >> + if (!err && !fscrypt_has_encryption_key(dir)) { >> + spin_lock(&dentry->d_lock); >> + dentry->d_flags |= DCACHE_NOKEY_NAME; >> + spin_unlock(&dentry->d_lock); >> + } >> + >> + return err; >> +} >> +EXPORT_SYMBOL_GPL(fscrypt_prepare_atomic_open); > [...] >> +static inline int fscrypt_prepare_atomic_open(struct inode *dir, >> + struct dentry *dentry) >> +{ >> + return -EOPNOTSUPP; >> +} > This has different behavior on unencrypted directories depending on whether > CONFIG_FS_ENCRYPTION is enabled or not. That's bad. > > In patch 2, the caller you are introducing has already checked IS_ENCRYPTED(). > > Also, your kerneldoc comment for fscrypt_prepare_atomic_open() says it is for > *encrypted* directories. > > So IMO, just remove the IS_ENCRYPTED() check from the CONFIG_FS_ENCRYPTION > version of fscrypt_prepare_atomic_open(). IMO we should keep this check in fscrypt_prepare_atomic_open() to make it consistent with the existing fscrypt_prepare_open(). And we can just remove the check from ceph instead. - Xiubo > - Eric >
On Tue, Mar 14, 2023 at 08:53:51AM +0800, Xiubo Li wrote: > > On 14/03/2023 02:09, Eric Biggers wrote: > > On Mon, Mar 13, 2023 at 12:33:09PM +0000, Luís Henriques wrote: > > > + * The regular open path will use fscrypt_file_open for that, but in the > > > + * atomic open a different approach is required. > > This should actually be fscrypt_prepare_lookup, not fscrypt_file_open, right? > > > > > +int fscrypt_prepare_atomic_open(struct inode *dir, struct dentry *dentry) > > > +{ > > > + int err; > > > + > > > + if (!IS_ENCRYPTED(dir)) > > > + return 0; > > > + > > > + err = fscrypt_get_encryption_info(dir, true); > > > + if (!err && !fscrypt_has_encryption_key(dir)) { > > > + spin_lock(&dentry->d_lock); > > > + dentry->d_flags |= DCACHE_NOKEY_NAME; > > > + spin_unlock(&dentry->d_lock); > > > + } > > > + > > > + return err; > > > +} > > > +EXPORT_SYMBOL_GPL(fscrypt_prepare_atomic_open); > > [...] > > > +static inline int fscrypt_prepare_atomic_open(struct inode *dir, > > > + struct dentry *dentry) > > > +{ > > > + return -EOPNOTSUPP; > > > +} > > This has different behavior on unencrypted directories depending on whether > > CONFIG_FS_ENCRYPTION is enabled or not. That's bad. > > > > In patch 2, the caller you are introducing has already checked IS_ENCRYPTED(). > > > > Also, your kerneldoc comment for fscrypt_prepare_atomic_open() says it is for > > *encrypted* directories. > > > > So IMO, just remove the IS_ENCRYPTED() check from the CONFIG_FS_ENCRYPTION > > version of fscrypt_prepare_atomic_open(). > > IMO we should keep this check in fscrypt_prepare_atomic_open() to make it > consistent with the existing fscrypt_prepare_open(). And we can just remove > the check from ceph instead. > Well, then the !CONFIG_FS_ENCRYPTION version would need to return 0 if IS_ENCRYPTED() too. Either way would be okay, but please don't do a mix of both approaches within a single function, as this patch currently does. Note that there are other fscrypt_* functions, such as fscrypt_get_symlink(), that require an IS_ENCRYPTED() inode, so that pattern is not new. - Eric
On 14/03/2023 10:25, Eric Biggers wrote: > On Tue, Mar 14, 2023 at 08:53:51AM +0800, Xiubo Li wrote: >> On 14/03/2023 02:09, Eric Biggers wrote: >>> On Mon, Mar 13, 2023 at 12:33:09PM +0000, Luís Henriques wrote: >>>> + * The regular open path will use fscrypt_file_open for that, but in the >>>> + * atomic open a different approach is required. >>> This should actually be fscrypt_prepare_lookup, not fscrypt_file_open, right? >>> >>>> +int fscrypt_prepare_atomic_open(struct inode *dir, struct dentry *dentry) >>>> +{ >>>> + int err; >>>> + >>>> + if (!IS_ENCRYPTED(dir)) >>>> + return 0; >>>> + >>>> + err = fscrypt_get_encryption_info(dir, true); >>>> + if (!err && !fscrypt_has_encryption_key(dir)) { >>>> + spin_lock(&dentry->d_lock); >>>> + dentry->d_flags |= DCACHE_NOKEY_NAME; >>>> + spin_unlock(&dentry->d_lock); >>>> + } >>>> + >>>> + return err; >>>> +} >>>> +EXPORT_SYMBOL_GPL(fscrypt_prepare_atomic_open); >>> [...] >>>> +static inline int fscrypt_prepare_atomic_open(struct inode *dir, >>>> + struct dentry *dentry) >>>> +{ >>>> + return -EOPNOTSUPP; >>>> +} >>> This has different behavior on unencrypted directories depending on whether >>> CONFIG_FS_ENCRYPTION is enabled or not. That's bad. >>> >>> In patch 2, the caller you are introducing has already checked IS_ENCRYPTED(). >>> >>> Also, your kerneldoc comment for fscrypt_prepare_atomic_open() says it is for >>> *encrypted* directories. >>> >>> So IMO, just remove the IS_ENCRYPTED() check from the CONFIG_FS_ENCRYPTION >>> version of fscrypt_prepare_atomic_open(). >> IMO we should keep this check in fscrypt_prepare_atomic_open() to make it >> consistent with the existing fscrypt_prepare_open(). And we can just remove >> the check from ceph instead. >> > Well, then the !CONFIG_FS_ENCRYPTION version would need to return 0 if > IS_ENCRYPTED() too. For the !CONFIG_FS_ENCRYPTION version I think you mean: static inline int fscrypt_prepare_atomic_open(struct inode *dir, struct dentry *dentry) { if (IS_ENCRYPTED(dir)) return -EOPNOTSUPP; return 0; } > Either way would be okay, but please don't do a mix of both approaches within a > single function, as this patch currently does. > > Note that there are other fscrypt_* functions, such as fscrypt_get_symlink(), > that require an IS_ENCRYPTED() inode, so that pattern is not new. Yeah, correct, I didn't notice that. - Xiubo > - Eric >
Xiubo Li <xiubli@redhat.com> writes: > On 14/03/2023 10:25, Eric Biggers wrote: >> On Tue, Mar 14, 2023 at 08:53:51AM +0800, Xiubo Li wrote: >>> On 14/03/2023 02:09, Eric Biggers wrote: >>>> On Mon, Mar 13, 2023 at 12:33:09PM +0000, Luís Henriques wrote: >>>>> + * The regular open path will use fscrypt_file_open for that, but in the >>>>> + * atomic open a different approach is required. >>>> This should actually be fscrypt_prepare_lookup, not fscrypt_file_open, right? >>>> >>>>> +int fscrypt_prepare_atomic_open(struct inode *dir, struct dentry *dentry) >>>>> +{ >>>>> + int err; >>>>> + >>>>> + if (!IS_ENCRYPTED(dir)) >>>>> + return 0; >>>>> + >>>>> + err = fscrypt_get_encryption_info(dir, true); >>>>> + if (!err && !fscrypt_has_encryption_key(dir)) { >>>>> + spin_lock(&dentry->d_lock); >>>>> + dentry->d_flags |= DCACHE_NOKEY_NAME; >>>>> + spin_unlock(&dentry->d_lock); >>>>> + } >>>>> + >>>>> + return err; >>>>> +} >>>>> +EXPORT_SYMBOL_GPL(fscrypt_prepare_atomic_open); >>>> [...] >>>>> +static inline int fscrypt_prepare_atomic_open(struct inode *dir, >>>>> + struct dentry *dentry) >>>>> +{ >>>>> + return -EOPNOTSUPP; >>>>> +} >>>> This has different behavior on unencrypted directories depending on whether >>>> CONFIG_FS_ENCRYPTION is enabled or not. That's bad. >>>> >>>> In patch 2, the caller you are introducing has already checked IS_ENCRYPTED(). >>>> >>>> Also, your kerneldoc comment for fscrypt_prepare_atomic_open() says it is for >>>> *encrypted* directories. >>>> >>>> So IMO, just remove the IS_ENCRYPTED() check from the CONFIG_FS_ENCRYPTION >>>> version of fscrypt_prepare_atomic_open(). >>> IMO we should keep this check in fscrypt_prepare_atomic_open() to make it >>> consistent with the existing fscrypt_prepare_open(). And we can just remove >>> the check from ceph instead. >>> >> Well, then the !CONFIG_FS_ENCRYPTION version would need to return 0 if >> IS_ENCRYPTED() too. > > For the !CONFIG_FS_ENCRYPTION version I think you mean: > > static inline int fscrypt_prepare_atomic_open(struct inode *dir, struct dentry > *dentry) > > { > if (IS_ENCRYPTED(dir)) > return -EOPNOTSUPP; > return 0; > } > > >> Either way would be okay, but please don't do a mix of both approaches within a >> single function, as this patch currently does. >> >> Note that there are other fscrypt_* functions, such as fscrypt_get_symlink(), >> that require an IS_ENCRYPTED() inode, so that pattern is not new. > > Yeah, correct, I didn't notice that. OK, thank you both for the feedback. I'll send out v2 in a few hours. But my preference will be to drop the IS_ENCRYPTED() from fscrypt_prepare_atomic_open(). The reason is that we still need to keep it in the caller function anyway, because we need to set the MDS flags accordingly (see patch 2): if (IS_ENCRYPTED(dir)) { set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags); err = fscrypt_prepare_atomic_open(dir, dentry); if (err) goto out_req; } Cheers,
Eric Biggers <ebiggers@kernel.org> writes: > On Mon, Mar 13, 2023 at 12:33:09PM +0000, Luís Henriques wrote: >> + * The regular open path will use fscrypt_file_open for that, but in the >> + * atomic open a different approach is required. > > This should actually be fscrypt_prepare_lookup, not fscrypt_file_open, right? Ups, I missed this comment. I was comparing the regular open() with the atomic_open() paths. I think I really mean fscrypt_file_open() because that's where the encryption info is (or may be) set by calling fscrypt_require_key(). atomic_open needs something similar, but combined with a lookup. Maybe I can rephrase it to: The reason for getting the encryption info before checking if the directory has the encryption key is because the key may be available but the encryption info isn't yet set (maybe due to a drop_caches). The regular open path will call fscrypt_file_open which uses function fscrypt_require_key for setting the encryption info if needed. The atomic open needs to do something similar. Cheers,
On Tue, Mar 14, 2023 at 10:15:11AM +0000, Luís Henriques wrote: > Eric Biggers <ebiggers@kernel.org> writes: > > > On Mon, Mar 13, 2023 at 12:33:09PM +0000, Luís Henriques wrote: > >> + * The regular open path will use fscrypt_file_open for that, but in the > >> + * atomic open a different approach is required. > > > > This should actually be fscrypt_prepare_lookup, not fscrypt_file_open, right? > > Ups, I missed this comment. > > I was comparing the regular open() with the atomic_open() paths. I think > I really mean fscrypt_file_open() because that's where the encryption info > is (or may be) set by calling fscrypt_require_key(). atomic_open needs > something similar, but combined with a lookup. > > Maybe I can rephrase it to: > > The reason for getting the encryption info before checking if the > directory has the encryption key is because the key may be available but > the encryption info isn't yet set (maybe due to a drop_caches). The > regular open path will call fscrypt_file_open which uses function > fscrypt_require_key for setting the encryption info if needed. The > atomic open needs to do something similar. > No, regular open is two parts: ->lookup and ->open. fscrypt_prepare_lookup() sets up the directory's key, whereas fscrypt_file_open() sets up the file's key. Your proposed fscrypt_prepare_atomic_open() sets up the directory's key. So it is really fscrypt_prepare_lookup() that is its equivalent. However, that raises the question of why doesn't ceph just use fscrypt_prepare_lookup()? It seems the answer is that ceph wants to handle the filenames encryption and no-key name encoding itself. And for that reason, its ->lookup() does the following and does *not* use fscrypt_prepare_lookup(): if (IS_ENCRYPTED(dir)) { err = ceph_fscrypt_prepare_readdir(dir); if (err < 0) return ERR_PTR(err); if (!fscrypt_has_encryption_key(dir)) { spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NOKEY_NAME; spin_unlock(&dentry->d_lock); } } So, actually I think this patch doesn't make sense. If ceph is doing the above in its ->lookup() anyway, then it just should do the exact same thing in its ->atomic_open() too. If you want to add a new fscrypt_* helper function which *just* sets up the given directory's key and sets the NOKEY_NAME flag on the given dentry accordingly, that could make sense. However, it should be called from *both* ->lookup() and ->atomic_open(), not just ->atomic_open(). It's also worth mentioning that setting up the filename separately from the NOKEY_NAME flag makes ceph have the same race condition that I had fixed for the other filesystems in commit b01531db6cec ("fscrypt: fix race where ->lookup() marks plaintext dentry as ciphertext"). It's not a huge deal, but it can cause some odd behavior, so it's worth thinking about whether it can be solved. - Eric
Eric Biggers <ebiggers@kernel.org> writes: > On Tue, Mar 14, 2023 at 10:15:11AM +0000, Luís Henriques wrote: >> Eric Biggers <ebiggers@kernel.org> writes: >> >> > On Mon, Mar 13, 2023 at 12:33:09PM +0000, Luís Henriques wrote: >> >> + * The regular open path will use fscrypt_file_open for that, but in the >> >> + * atomic open a different approach is required. >> > >> > This should actually be fscrypt_prepare_lookup, not fscrypt_file_open, right? >> >> Ups, I missed this comment. >> >> I was comparing the regular open() with the atomic_open() paths. I think >> I really mean fscrypt_file_open() because that's where the encryption info >> is (or may be) set by calling fscrypt_require_key(). atomic_open needs >> something similar, but combined with a lookup. >> >> Maybe I can rephrase it to: >> >> The reason for getting the encryption info before checking if the >> directory has the encryption key is because the key may be available but >> the encryption info isn't yet set (maybe due to a drop_caches). The >> regular open path will call fscrypt_file_open which uses function >> fscrypt_require_key for setting the encryption info if needed. The >> atomic open needs to do something similar. >> > > No, regular open is two parts: ->lookup and ->open. fscrypt_prepare_lookup() > sets up the directory's key, whereas fscrypt_file_open() sets up the file's key. > > Your proposed fscrypt_prepare_atomic_open() sets up the directory's key. So it > is really fscrypt_prepare_lookup() that is its equivalent. Oh, I see what you mean now, and you're obviously correct. Thanks for the detailed explanation. > However, that raises the question of why doesn't ceph just use > fscrypt_prepare_lookup()? It seems the answer is that ceph wants to handle the > filenames encryption and no-key name encoding itself. And for that reason, its > ->lookup() does the following and does *not* use fscrypt_prepare_lookup(): > > if (IS_ENCRYPTED(dir)) { > err = ceph_fscrypt_prepare_readdir(dir); > if (err < 0) > return ERR_PTR(err); > if (!fscrypt_has_encryption_key(dir)) { > spin_lock(&dentry->d_lock); > dentry->d_flags |= DCACHE_NOKEY_NAME; > spin_unlock(&dentry->d_lock); > } > } Ugh, I tend to forget all the details behind these decisions. If I remember correctly, we had to work around the fact that the cephfs client handle directory data in a cumbersome way. We may not have the full data for a readdir, for example, and that has to be handled by a lookup. > So, actually I think this patch doesn't make sense. If ceph is doing the above > in its ->lookup() anyway, then it just should do the exact same thing in its > ->atomic_open() too. In fact, my initial fix for the cephfs bug was doing just that. It was a single patch to ceph_atomic_open() that would simply do: if (IS_ENCRYPTED(dir)) { set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags); err = __fscrypt_prepare_readdir(dir); if (!err && !fscrypt_has_encryption_key(dir)) { spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NOKEY_NAME; spin_unlock(&dentry->d_lock); } } What made me want to create a new helper was that I simply needed to call fscrypt_get_encryption_info() to force the encryption info to be set in the parent directory. But this function was only accessible through __fscrypt_prepare_readdir(), which isn't really a great function name for what I need here. Since __fscrypt_prepare_readdir() doesn't seem to be used anywhere else, maybe it could be removed and fscrypt_get_encryption_info() be exported instead? > If you want to add a new fscrypt_* helper function which *just* sets up the > given directory's key and sets the NOKEY_NAME flag on the given dentry > accordingly, that could make sense. However, it should be called from *both* > ->lookup() and ->atomic_open(), not just ->atomic_open(). > > It's also worth mentioning that setting up the filename separately from the > NOKEY_NAME flag makes ceph have the same race condition that I had fixed for the > other filesystems in commit b01531db6cec ("fscrypt: fix race where ->lookup() > marks plaintext dentry as ciphertext"). It's not a huge deal, but it can cause > some odd behavior, so it's worth thinking about whether it can be solved. Hmm... OK, looks like we'll need to have a look into this. Thanks for the heads-up. Cheers,
On Wed, Mar 15, 2023 at 11:08:23AM +0000, Luís Henriques wrote: > > So, actually I think this patch doesn't make sense. If ceph is doing the above > > in its ->lookup() anyway, then it just should do the exact same thing in its > > ->atomic_open() too. > > In fact, my initial fix for the cephfs bug was doing just that. It was a > single patch to ceph_atomic_open() that would simply do: > > if (IS_ENCRYPTED(dir)) { > set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags); > err = __fscrypt_prepare_readdir(dir); > if (!err && !fscrypt_has_encryption_key(dir)) { > spin_lock(&dentry->d_lock); > dentry->d_flags |= DCACHE_NOKEY_NAME; > spin_unlock(&dentry->d_lock); > } > } > > What made me want to create a new helper was that I simply needed to call > fscrypt_get_encryption_info() to force the encryption info to be set in > the parent directory. But this function was only accessible through > __fscrypt_prepare_readdir(), which isn't really a great function name for > what I need here. > > Since __fscrypt_prepare_readdir() doesn't seem to be used anywhere else, > maybe it could be removed and fscrypt_get_encryption_info() be exported > instead? Well, fscrypt_get_encryption_info() *used* to be exported, but it was hard to keep track of its use cases (some of which were not actually necessary), which is why it eventually got replaced with use-case oriented helper functions. Maybe just use fscrypt_prepare_lookup_partial() for the name of your new helper function (instead of fscrypt_prepare_atomic_open())? - Eric
Eric Biggers <ebiggers@kernel.org> writes: > On Wed, Mar 15, 2023 at 11:08:23AM +0000, Luís Henriques wrote: >> > So, actually I think this patch doesn't make sense. If ceph is doing the above >> > in its ->lookup() anyway, then it just should do the exact same thing in its >> > ->atomic_open() too. >> >> In fact, my initial fix for the cephfs bug was doing just that. It was a >> single patch to ceph_atomic_open() that would simply do: >> >> if (IS_ENCRYPTED(dir)) { >> set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags); >> err = __fscrypt_prepare_readdir(dir); >> if (!err && !fscrypt_has_encryption_key(dir)) { >> spin_lock(&dentry->d_lock); >> dentry->d_flags |= DCACHE_NOKEY_NAME; >> spin_unlock(&dentry->d_lock); >> } >> } >> >> What made me want to create a new helper was that I simply needed to call >> fscrypt_get_encryption_info() to force the encryption info to be set in >> the parent directory. But this function was only accessible through >> __fscrypt_prepare_readdir(), which isn't really a great function name for >> what I need here. >> >> Since __fscrypt_prepare_readdir() doesn't seem to be used anywhere else, >> maybe it could be removed and fscrypt_get_encryption_info() be exported >> instead? > > Well, fscrypt_get_encryption_info() *used* to be exported, but it was hard to > keep track of its use cases (some of which were not actually necessary), which > is why it eventually got replaced with use-case oriented helper functions. > > Maybe just use fscrypt_prepare_lookup_partial() for the name of your new helper > function (instead of fscrypt_prepare_atomic_open())? OK, thanks for the name suggestion (naming is *indeed* hard). I'll go try to get a new helper that can be used in both open_atomic and lookup. That'll require a bit more of testing so that I don't end up breaking something else. Cheers,
diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 7b8c5a1104b5..8be1e35984f1 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -117,6 +117,41 @@ int __fscrypt_prepare_readdir(struct inode *dir) } EXPORT_SYMBOL_GPL(__fscrypt_prepare_readdir); +/** + * fscrypt_prepare_atomic_open() - prepare an atomic open on an encrypted directory + * @dir: inode of parent directory + * @dentry: dentry being open + * + * Because atomic open can act as a lookup if handed a dentry that is negative, + * we need to set DCACHE_NOKEY_NAME if the key for the parent isn't available. + * + * The reason for getting the encryption info before checking if the directory + * has the encryption key is because the key may be available but the encryption + * info isn't yet set (maybe due to a drop_caches). The regular open path will + * use fscrypt_file_open for that, but in the atomic open a different approach + * is required. + * + * Return: 0 on success, or an error code if fscrypt_get_encryption_info() + * fails. + */ +int fscrypt_prepare_atomic_open(struct inode *dir, struct dentry *dentry) +{ + int err; + + if (!IS_ENCRYPTED(dir)) + return 0; + + err = fscrypt_get_encryption_info(dir, true); + if (!err && !fscrypt_has_encryption_key(dir)) { + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_NOKEY_NAME; + spin_unlock(&dentry->d_lock); + } + + return err; +} +EXPORT_SYMBOL_GPL(fscrypt_prepare_atomic_open); + int __fscrypt_prepare_setattr(struct dentry *dentry, struct iattr *attr) { if (attr->ia_valid & ATTR_SIZE) diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 4f5f8a651213..c70acb2a737a 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -362,6 +362,7 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry, struct fscrypt_name *fname); int __fscrypt_prepare_readdir(struct inode *dir); +int fscrypt_prepare_atomic_open(struct inode *dir, struct dentry *dentry); int __fscrypt_prepare_setattr(struct dentry *dentry, struct iattr *attr); int fscrypt_prepare_setflags(struct inode *inode, unsigned int oldflags, unsigned int flags); @@ -688,6 +689,12 @@ static inline int __fscrypt_prepare_readdir(struct inode *dir) return -EOPNOTSUPP; } +static inline int fscrypt_prepare_atomic_open(struct inode *dir, + struct dentry *dentry) +{ + return -EOPNOTSUPP; +} + static inline int __fscrypt_prepare_setattr(struct dentry *dentry, struct iattr *attr) {