162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include "ubifs.h"
362306a36Sopenharmony_ci
462306a36Sopenharmony_cistatic int ubifs_crypt_get_context(struct inode *inode, void *ctx, size_t len)
562306a36Sopenharmony_ci{
662306a36Sopenharmony_ci	return ubifs_xattr_get(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT,
762306a36Sopenharmony_ci			       ctx, len);
862306a36Sopenharmony_ci}
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistatic int ubifs_crypt_set_context(struct inode *inode, const void *ctx,
1162306a36Sopenharmony_ci				   size_t len, void *fs_data)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	/*
1462306a36Sopenharmony_ci	 * Creating an encryption context is done unlocked since we
1562306a36Sopenharmony_ci	 * operate on a new inode which is not visible to other users
1662306a36Sopenharmony_ci	 * at this point. So, no need to check whether inode is locked.
1762306a36Sopenharmony_ci	 */
1862306a36Sopenharmony_ci	return ubifs_xattr_set(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT,
1962306a36Sopenharmony_ci			       ctx, len, 0, false);
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic bool ubifs_crypt_empty_dir(struct inode *inode)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	return ubifs_check_dir_empty(inode) == 0;
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/**
2862306a36Sopenharmony_ci * ubifs_encrypt - Encrypt data.
2962306a36Sopenharmony_ci * @inode: inode which refers to the data node
3062306a36Sopenharmony_ci * @dn: data node to encrypt
3162306a36Sopenharmony_ci * @in_len: length of data to be compressed
3262306a36Sopenharmony_ci * @out_len: allocated memory size for the data area of @dn
3362306a36Sopenharmony_ci * @block: logical block number of the block
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * This function encrypt a possibly-compressed data in the data node.
3662306a36Sopenharmony_ci * The encrypted data length will store in @out_len.
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_ciint ubifs_encrypt(const struct inode *inode, struct ubifs_data_node *dn,
3962306a36Sopenharmony_ci		  unsigned int in_len, unsigned int *out_len, int block)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct ubifs_info *c = inode->i_sb->s_fs_info;
4262306a36Sopenharmony_ci	void *p = &dn->data;
4362306a36Sopenharmony_ci	unsigned int pad_len = round_up(in_len, UBIFS_CIPHER_BLOCK_SIZE);
4462306a36Sopenharmony_ci	int err;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	ubifs_assert(c, pad_len <= *out_len);
4762306a36Sopenharmony_ci	dn->compr_size = cpu_to_le16(in_len);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* pad to full block cipher length */
5062306a36Sopenharmony_ci	if (pad_len != in_len)
5162306a36Sopenharmony_ci		memset(p + in_len, 0, pad_len - in_len);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	err = fscrypt_encrypt_block_inplace(inode, virt_to_page(p), pad_len,
5462306a36Sopenharmony_ci					    offset_in_page(p), block, GFP_NOFS);
5562306a36Sopenharmony_ci	if (err) {
5662306a36Sopenharmony_ci		ubifs_err(c, "fscrypt_encrypt_block_inplace() failed: %d", err);
5762306a36Sopenharmony_ci		return err;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci	*out_len = pad_len;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return 0;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ciint ubifs_decrypt(const struct inode *inode, struct ubifs_data_node *dn,
6562306a36Sopenharmony_ci		  unsigned int *out_len, int block)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct ubifs_info *c = inode->i_sb->s_fs_info;
6862306a36Sopenharmony_ci	int err;
6962306a36Sopenharmony_ci	unsigned int clen = le16_to_cpu(dn->compr_size);
7062306a36Sopenharmony_ci	unsigned int dlen = *out_len;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (clen <= 0 || clen > UBIFS_BLOCK_SIZE || clen > dlen) {
7362306a36Sopenharmony_ci		ubifs_err(c, "bad compr_size: %i", clen);
7462306a36Sopenharmony_ci		return -EINVAL;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	ubifs_assert(c, dlen <= UBIFS_BLOCK_SIZE);
7862306a36Sopenharmony_ci	err = fscrypt_decrypt_block_inplace(inode, virt_to_page(&dn->data),
7962306a36Sopenharmony_ci					    dlen, offset_in_page(&dn->data),
8062306a36Sopenharmony_ci					    block);
8162306a36Sopenharmony_ci	if (err) {
8262306a36Sopenharmony_ci		ubifs_err(c, "fscrypt_decrypt_block_inplace() failed: %d", err);
8362306a36Sopenharmony_ci		return err;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	*out_len = clen;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ciconst struct fscrypt_operations ubifs_crypt_operations = {
9162306a36Sopenharmony_ci	.flags			= FS_CFLG_OWN_PAGES,
9262306a36Sopenharmony_ci	.key_prefix		= "ubifs:",
9362306a36Sopenharmony_ci	.get_context		= ubifs_crypt_get_context,
9462306a36Sopenharmony_ci	.set_context		= ubifs_crypt_set_context,
9562306a36Sopenharmony_ci	.empty_dir		= ubifs_crypt_empty_dir,
9662306a36Sopenharmony_ci};
97