diff options
Diffstat (limited to 'fs/crypto/inline_crypt.c')
| -rw-r--r-- | fs/crypto/inline_crypt.c | 196 | 
1 files changed, 95 insertions, 101 deletions
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 90f3e68f166e..cea8b14007e6 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -21,26 +21,22 @@  #include "fscrypt_private.h" -struct fscrypt_blk_crypto_key { -	struct blk_crypto_key base; -	int num_devs; -	struct request_queue *devs[]; -}; - -static int fscrypt_get_num_devices(struct super_block *sb) +static struct block_device **fscrypt_get_devices(struct super_block *sb, +						 unsigned int *num_devs)  { -	if (sb->s_cop->get_num_devices) -		return sb->s_cop->get_num_devices(sb); -	return 1; -} +	struct block_device **devs; -static void fscrypt_get_devices(struct super_block *sb, int num_devs, -				struct request_queue **devs) -{ -	if (num_devs == 1) -		devs[0] = bdev_get_queue(sb->s_bdev); -	else -		sb->s_cop->get_devices(sb, devs); +	if (sb->s_cop->get_devices) { +		devs = sb->s_cop->get_devices(sb, num_devs); +		if (devs) +			return devs; +	} +	devs = kmalloc(sizeof(*devs), GFP_KERNEL); +	if (!devs) +		return ERR_PTR(-ENOMEM); +	devs[0] = sb->s_bdev; +	*num_devs = 1; +	return devs;  }  static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_info *ci) @@ -74,15 +70,17 @@ static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_info *ci)   * helpful for debugging problems where the "wrong" implementation is used.   */  static void fscrypt_log_blk_crypto_impl(struct fscrypt_mode *mode, -					struct request_queue **devs, -					int num_devs, +					struct block_device **devs, +					unsigned int num_devs,  					const struct blk_crypto_config *cfg)  { -	int i; +	unsigned int i;  	for (i = 0; i < num_devs; i++) { +		struct request_queue *q = bdev_get_queue(devs[i]); +  		if (!IS_ENABLED(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) || -		    __blk_crypto_cfg_supported(devs[i]->crypto_profile, cfg)) { +		    __blk_crypto_cfg_supported(q->crypto_profile, cfg)) {  			if (!xchg(&mode->logged_blk_crypto_native, 1))  				pr_info("fscrypt: %s using blk-crypto (native)\n",  					mode->friendly_name); @@ -99,9 +97,9 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci)  	const struct inode *inode = ci->ci_inode;  	struct super_block *sb = inode->i_sb;  	struct blk_crypto_config crypto_cfg; -	int num_devs; -	struct request_queue **devs; -	int i; +	struct block_device **devs; +	unsigned int num_devs; +	unsigned int i;  	/* The file must need contents encryption, not filenames encryption */  	if (!S_ISREG(inode->i_mode)) @@ -129,20 +127,20 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci)  		return 0;  	/* -	 * On all the filesystem's devices, blk-crypto must support the crypto -	 * configuration that the file would use. +	 * On all the filesystem's block devices, blk-crypto must support the +	 * crypto configuration that the file would use.  	 */  	crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode;  	crypto_cfg.data_unit_size = sb->s_blocksize;  	crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci); -	num_devs = fscrypt_get_num_devices(sb); -	devs = kmalloc_array(num_devs, sizeof(*devs), GFP_KERNEL); -	if (!devs) -		return -ENOMEM; -	fscrypt_get_devices(sb, num_devs, devs); + +	devs = fscrypt_get_devices(sb, &num_devs); +	if (IS_ERR(devs)) +		return PTR_ERR(devs);  	for (i = 0; i < num_devs; i++) { -		if (!blk_crypto_config_supported(devs[i], &crypto_cfg)) +		if (!blk_crypto_config_supported(bdev_get_queue(devs[i]), +						 &crypto_cfg))  			goto out_free_devs;  	} @@ -162,49 +160,41 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,  	const struct inode *inode = ci->ci_inode;  	struct super_block *sb = inode->i_sb;  	enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode; -	int num_devs = fscrypt_get_num_devices(sb); -	int queue_refs = 0; -	struct fscrypt_blk_crypto_key *blk_key; +	struct blk_crypto_key *blk_key; +	struct block_device **devs; +	unsigned int num_devs; +	unsigned int i;  	int err; -	int i; -	blk_key = kzalloc(struct_size(blk_key, devs, num_devs), GFP_KERNEL); +	blk_key = kmalloc(sizeof(*blk_key), GFP_KERNEL);  	if (!blk_key)  		return -ENOMEM; -	blk_key->num_devs = num_devs; -	fscrypt_get_devices(sb, num_devs, blk_key->devs); - -	err = blk_crypto_init_key(&blk_key->base, raw_key, crypto_mode, +	err = blk_crypto_init_key(blk_key, raw_key, crypto_mode,  				  fscrypt_get_dun_bytes(ci), sb->s_blocksize);  	if (err) {  		fscrypt_err(inode, "error %d initializing blk-crypto key", err);  		goto fail;  	} -	/* -	 * We have to start using blk-crypto on all the filesystem's devices. -	 * We also have to save all the request_queue's for later so that the -	 * key can be evicted from them.  This is needed because some keys -	 * aren't destroyed until after the filesystem was already unmounted -	 * (namely, the per-mode keys in struct fscrypt_master_key). -	 */ +	/* Start using blk-crypto on all the filesystem's block devices. */ +	devs = fscrypt_get_devices(sb, &num_devs); +	if (IS_ERR(devs)) { +		err = PTR_ERR(devs); +		goto fail; +	}  	for (i = 0; i < num_devs; i++) { -		if (!blk_get_queue(blk_key->devs[i])) { -			fscrypt_err(inode, "couldn't get request_queue"); -			err = -EAGAIN; -			goto fail; -		} -		queue_refs++; - -		err = blk_crypto_start_using_key(&blk_key->base, -						 blk_key->devs[i]); -		if (err) { -			fscrypt_err(inode, -				    "error %d starting to use blk-crypto", err); -			goto fail; -		} +		err = blk_crypto_start_using_key(blk_key, +						 bdev_get_queue(devs[i])); +		if (err) +			break;  	} +	kfree(devs); +	if (err) { +		fscrypt_err(inode, "error %d starting to use blk-crypto", err); +		goto fail; +	} +  	/*  	 * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared().  	 * I.e., here we publish ->blk_key with a RELEASE barrier so that @@ -215,24 +205,29 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,  	return 0;  fail: -	for (i = 0; i < queue_refs; i++) -		blk_put_queue(blk_key->devs[i]);  	kfree_sensitive(blk_key);  	return err;  } -void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key) +void fscrypt_destroy_inline_crypt_key(struct super_block *sb, +				      struct fscrypt_prepared_key *prep_key)  { -	struct fscrypt_blk_crypto_key *blk_key = prep_key->blk_key; -	int i; +	struct blk_crypto_key *blk_key = prep_key->blk_key; +	struct block_device **devs; +	unsigned int num_devs; +	unsigned int i; -	if (blk_key) { -		for (i = 0; i < blk_key->num_devs; i++) { -			blk_crypto_evict_key(blk_key->devs[i], &blk_key->base); -			blk_put_queue(blk_key->devs[i]); -		} -		kfree_sensitive(blk_key); +	if (!blk_key) +		return; + +	/* Evict the key from all the filesystem's block devices. */ +	devs = fscrypt_get_devices(sb, &num_devs); +	if (!IS_ERR(devs)) { +		for (i = 0; i < num_devs; i++) +			blk_crypto_evict_key(bdev_get_queue(devs[i]), blk_key); +		kfree(devs);  	} +	kfree_sensitive(blk_key);  }  bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode) @@ -282,7 +277,7 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode,  	ci = inode->i_crypt_info;  	fscrypt_generate_dun(ci, first_lblk, dun); -	bio_crypt_set_ctx(bio, &ci->ci_enc_key.blk_key->base, dun, gfp_mask); +	bio_crypt_set_ctx(bio, ci->ci_enc_key.blk_key, dun, gfp_mask);  }  EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx); @@ -369,7 +364,7 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,  	 * uses the same pointer.  I.e., there's currently no need to support  	 * merging requests where the keys are the same but the pointers differ.  	 */ -	if (bc->bc_key != &inode->i_crypt_info->ci_enc_key.blk_key->base) +	if (bc->bc_key != inode->i_crypt_info->ci_enc_key.blk_key)  		return false;  	fscrypt_generate_dun(inode->i_crypt_info, next_lblk, next_dun); @@ -401,46 +396,45 @@ bool fscrypt_mergeable_bio_bh(struct bio *bio,  EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio_bh);  /** - * fscrypt_dio_supported() - check whether a DIO (direct I/O) request is - *			     supported as far as encryption is concerned - * @iocb: the file and position the I/O is targeting - * @iter: the I/O data segment(s) + * fscrypt_dio_supported() - check whether DIO (direct I/O) is supported on an + *			     inode, as far as encryption is concerned + * @inode: the inode in question   *   * Return: %true if there are no encryption constraints that prevent DIO from   *	   being supported; %false if DIO is unsupported.  (Note that in the   *	   %true case, the filesystem might have other, non-encryption-related - *	   constraints that prevent DIO from actually being supported.) + *	   constraints that prevent DIO from actually being supported.  Also, on + *	   encrypted files the filesystem is still responsible for only allowing + *	   DIO when requests are filesystem-block-aligned.)   */ -bool fscrypt_dio_supported(struct kiocb *iocb, struct iov_iter *iter) +bool fscrypt_dio_supported(struct inode *inode)  { -	const struct inode *inode = file_inode(iocb->ki_filp); -	const unsigned int blocksize = i_blocksize(inode); +	int err;  	/* If the file is unencrypted, no veto from us. */  	if (!fscrypt_needs_contents_encryption(inode))  		return true; -	/* We only support DIO with inline crypto, not fs-layer crypto. */ -	if (!fscrypt_inode_uses_inline_crypto(inode)) -		return false; -  	/* -	 * Since the granularity of encryption is filesystem blocks, the file -	 * position and total I/O length must be aligned to the filesystem block -	 * size -- not just to the block device's logical block size as is -	 * traditionally the case for DIO on many filesystems. +	 * We only support DIO with inline crypto, not fs-layer crypto.  	 * -	 * We require that the user-provided memory buffers be filesystem block -	 * aligned too.  It is simpler to have a single alignment value required -	 * for all properties of the I/O, as is normally the case for DIO. -	 * Also, allowing less aligned buffers would imply that data units could -	 * cross bvecs, which would greatly complicate the I/O stack, which -	 * assumes that bios can be split at any bvec boundary. +	 * To determine whether the inode is using inline crypto, we have to set +	 * up the key if it wasn't already done.  This is because in the current +	 * design of fscrypt, the decision of whether to use inline crypto or +	 * not isn't made until the inode's encryption key is being set up.  In +	 * the DIO read/write case, the key will always be set up already, since +	 * the file will be open.  But in the case of statx(), the key might not +	 * be set up yet, as the file might not have been opened yet.  	 */ -	if (!IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(iter), blocksize)) +	err = fscrypt_require_key(inode); +	if (err) { +		/* +		 * Key unavailable or couldn't be set up.  This edge case isn't +		 * worth worrying about; just report that DIO is unsupported. +		 */  		return false; - -	return true; +	} +	return fscrypt_inode_uses_inline_crypto(inode);  }  EXPORT_SYMBOL_GPL(fscrypt_dio_supported);  |