Unverified Commit 93221de3 authored by Eric Biggers's avatar Eric Biggers Committed by Christian Brauner
Browse files

fscrypt: add support for info in fs-specific part of inode



Add an inode_info_offs field to struct fscrypt_operations, and update
fs/crypto/ to support it.  When set to a nonzero value, it specifies the
offset to the fscrypt_inode_info pointer within the filesystem-specific
part of the inode structure, to be used instead of inode::i_crypt_info.

Since this makes inode::i_crypt_info no longer necessarily used, update
comments that mentioned it.

This is a prerequisite for a later commit that removes
inode::i_crypt_info, saving memory and improving cache efficiency with
filesystems that don't support fscrypt.

Co-developed-by: default avatarChristian Brauner <brauner@kernel.org>
Signed-off-by: default avatarEric Biggers <ebiggers@kernel.org>
Link: https://lore.kernel.org/20250810075706.172910-3-ebiggers@kernel.org


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 6c9468aa
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -249,8 +249,8 @@ struct fscrypt_prepared_key {
 * fscrypt_inode_info - the "encryption key" for an inode
 *
 * When an encrypted file's key is made available, an instance of this struct is
 * allocated and stored in ->i_crypt_info.  Once created, it remains until the
 * inode is evicted.
 * allocated and a pointer to it is stored in the file's in-memory inode.  Once
 * created, it remains until the inode is evicted.
 */
struct fscrypt_inode_info {

+26 −17
Original line number Diff line number Diff line
@@ -642,15 +642,16 @@ fscrypt_setup_encryption_info(struct inode *inode,
		goto out;

	/*
	 * For existing inodes, multiple tasks may race to set ->i_crypt_info.
	 * So use cmpxchg_release().  This pairs with the smp_load_acquire() in
	 * fscrypt_get_inode_info().  I.e., here we publish ->i_crypt_info with
	 * a RELEASE barrier so that other tasks can ACQUIRE it.
	 * For existing inodes, multiple tasks may race to set the inode's
	 * fscrypt info pointer.  So use cmpxchg_release().  This pairs with the
	 * smp_load_acquire() in fscrypt_get_inode_info().  I.e., publish the
	 * pointer with a RELEASE barrier so that other tasks can ACQUIRE it.
	 */
	if (cmpxchg_release(&inode->i_crypt_info, NULL, crypt_info) == NULL) {
	if (cmpxchg_release(fscrypt_inode_info_addr(inode), NULL, crypt_info) ==
	    NULL) {
		/*
		 * We won the race and set ->i_crypt_info to our crypt_info.
		 * Now link it into the master key's inode list.
		 * We won the race and set the inode's fscrypt info to our
		 * crypt_info.  Now link it into the master key's inode list.
		 */
		if (mk) {
			crypt_info->ci_master_key = mk;
@@ -681,13 +682,13 @@ fscrypt_setup_encryption_info(struct inode *inode,
 *		       %false unless the operation being performed is needed in
 *		       order for files (or directories) to be deleted.
 *
 * Set up ->i_crypt_info, if it hasn't already been done.
 * Set up the inode's encryption key, if it hasn't already been done.
 *
 * Note: unless ->i_crypt_info is already set, this isn't %GFP_NOFS-safe.  So
 * Note: unless the key setup was already done, this isn't %GFP_NOFS-safe.  So
 * generally this shouldn't be called from within a filesystem transaction.
 *
 * Return: 0 if ->i_crypt_info was set or was already set, *or* if the
 *	   encryption key is unavailable.  (Use fscrypt_has_encryption_key() to
 * Return: 0 if the key is now set up, *or* if it couldn't be set up because the
 *	   needed master key is absent.  (Use fscrypt_has_encryption_key() to
 *	   distinguish these cases.)  Also can return another -errno code.
 */
int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported)
@@ -741,9 +742,9 @@ int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported)
 *	   ->i_ino doesn't need to be set yet.
 * @encrypt_ret: (output) set to %true if the new inode will be encrypted
 *
 * If the directory is encrypted, set up its ->i_crypt_info in preparation for
 * If the directory is encrypted, set up its encryption key in preparation for
 * encrypting the name of the new file.  Also, if the new inode will be
 * encrypted, set up its ->i_crypt_info and set *encrypt_ret=true.
 * encrypted, set up its encryption key too and set *encrypt_ret=true.
 *
 * This isn't %GFP_NOFS-safe, and therefore it should be called before starting
 * any filesystem transaction to create the inode.  For this reason, ->i_ino
@@ -752,8 +753,8 @@ int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported)
 * This doesn't persist the new inode's encryption context.  That still needs to
 * be done later by calling fscrypt_set_context().
 *
 * Return: 0 on success, -ENOKEY if the encryption key is missing, or another
 *	   -errno code
 * Return: 0 on success, -ENOKEY if a key needs to be set up for @dir or @inode
 *	   but the needed master key is absent, or another -errno code
 */
int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode,
			      bool *encrypt_ret)
@@ -800,8 +801,16 @@ EXPORT_SYMBOL_GPL(fscrypt_prepare_new_inode);
 */
void fscrypt_put_encryption_info(struct inode *inode)
{
	put_crypt_info(inode->i_crypt_info);
	inode->i_crypt_info = NULL;
	/*
	 * Ideally we'd start with a lightweight IS_ENCRYPTED() check here
	 * before proceeding to retrieve and check the pointer.  However, during
	 * inode creation, the fscrypt_inode_info is set before S_ENCRYPTED.  If
	 * an error occurs, it needs to be cleaned up regardless.
	 */
	struct fscrypt_inode_info **ci_addr = fscrypt_inode_info_addr(inode);

	put_crypt_info(*ci_addr);
	*ci_addr = NULL;
}
EXPORT_SYMBOL(fscrypt_put_encryption_info);

+18 −4
Original line number Diff line number Diff line
@@ -61,6 +61,12 @@ struct fscrypt_name {

/* Crypto operations for filesystems */
struct fscrypt_operations {
	/*
	 * The offset of the pointer to struct fscrypt_inode_info in the
	 * filesystem-specific part of the inode, relative to the beginning of
	 * the common part of the inode (the 'struct inode').
	 */
	ptrdiff_t inode_info_offs;

	/*
	 * If set, then fs/crypto/ will allocate a global bounce page pool the
@@ -195,6 +201,14 @@ struct fscrypt_operations {
int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name,
			 struct dentry *dentry, unsigned int flags);

static inline struct fscrypt_inode_info **
fscrypt_inode_info_addr(const struct inode *inode)
{
	if (inode->i_sb->s_cop->inode_info_offs == 0)
		return (struct fscrypt_inode_info **)&inode->i_crypt_info;
	return (void *)inode + inode->i_sb->s_cop->inode_info_offs;
}

/*
 * Load the inode's fscrypt info pointer, using a raw dereference.  Since this
 * uses a raw dereference with no memory barrier, it is appropriate to use only
@@ -205,7 +219,7 @@ int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name,
static inline struct fscrypt_inode_info *
fscrypt_get_inode_info_raw(const struct inode *inode)
{
	struct fscrypt_inode_info *ci = inode->i_crypt_info;
	struct fscrypt_inode_info *ci = *fscrypt_inode_info_addr(inode);

	VFS_WARN_ON_ONCE(ci == NULL);
	return ci;
@@ -216,11 +230,11 @@ fscrypt_get_inode_info(const struct inode *inode)
{
	/*
	 * Pairs with the cmpxchg_release() in fscrypt_setup_encryption_info().
	 * I.e., another task may publish ->i_crypt_info concurrently, executing
	 * a RELEASE barrier.  We need to use smp_load_acquire() here to safely
	 * I.e., another task may publish the fscrypt info concurrently,
	 * executing a RELEASE barrier.  Use smp_load_acquire() here to safely
	 * ACQUIRE the memory the other task published.
	 */
	return smp_load_acquire(&inode->i_crypt_info);
	return smp_load_acquire(fscrypt_inode_info_addr(inode));
}

/**