You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 updates from Ted Ts'o: "A few bug fixes and add support for file-system level encryption in ext4" * tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (31 commits) ext4 crypto: enable encryption feature flag ext4 crypto: add symlink encryption ext4 crypto: enable filename encryption ext4 crypto: filename encryption modifications ext4 crypto: partial update to namei.c for fname crypto ext4 crypto: insert encrypted filenames into a leaf directory block ext4 crypto: teach ext4_htree_store_dirent() to store decrypted filenames ext4 crypto: filename encryption facilities ext4 crypto: implement the ext4 decryption read path ext4 crypto: implement the ext4 encryption write path ext4 crypto: inherit encryption policies on inode and directory create ext4 crypto: enforce context consistency ext4 crypto: add encryption key management facilities ext4 crypto: add ext4 encryption facilities ext4 crypto: add encryption policy and password salt support ext4 crypto: add encryption xattr support ext4 crypto: export ext4_empty_dir() ext4 crypto: add ext4 encryption Kconfig ext4 crypto: reserve codepoints used by the ext4 encryption feature ext4 crypto: add ext4_mpage_readpages() ...
This commit is contained in:
@@ -64,6 +64,23 @@ config EXT4_FS_SECURITY
|
||||
If you are not using a security module that requires using
|
||||
extended attributes for file security labels, say N.
|
||||
|
||||
config EXT4_FS_ENCRYPTION
|
||||
bool "Ext4 Encryption"
|
||||
depends on EXT4_FS
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_XTS
|
||||
select CRYPTO_CTS
|
||||
select CRYPTO_SHA256
|
||||
select KEYS
|
||||
select ENCRYPTED_KEYS
|
||||
help
|
||||
Enable encryption of ext4 files and directories. This
|
||||
feature is similar to ecryptfs, but it is more memory
|
||||
efficient since it avoids caching the encrypted and
|
||||
decrypted pages in the page cache.
|
||||
|
||||
config EXT4_DEBUG
|
||||
bool "EXT4 debugging support"
|
||||
depends on EXT4_FS
|
||||
|
||||
+3
-1
@@ -8,7 +8,9 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
|
||||
ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \
|
||||
ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \
|
||||
mmp.o indirect.o extents_status.o xattr.o xattr_user.o \
|
||||
xattr_trusted.o inline.o
|
||||
xattr_trusted.o inline.o readpage.o
|
||||
|
||||
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
|
||||
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
|
||||
ext4-$(CONFIG_EXT4_FS_ENCRYPTION) += crypto_policy.o crypto.o \
|
||||
crypto_key.o crypto_fname.o
|
||||
|
||||
@@ -4,11 +4,6 @@
|
||||
* Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/fs.h>
|
||||
#include "ext4_jbd2.h"
|
||||
#include "ext4.h"
|
||||
#include "xattr.h"
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <linux/time.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/jbd2.h>
|
||||
#include <linux/quotaops.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include "ext4.h"
|
||||
@@ -641,8 +640,6 @@ ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode,
|
||||
* fail EDQUOT for metdata, but we do account for it.
|
||||
*/
|
||||
if (!(*errp) && (flags & EXT4_MB_DELALLOC_RESERVED)) {
|
||||
spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
|
||||
spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
|
||||
dquot_alloc_block_nofail(inode,
|
||||
EXT4_C2B(EXT4_SB(inode->i_sb), ar.len));
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/jbd2.h>
|
||||
#include "ext4.h"
|
||||
|
||||
unsigned int ext4_count_free(char *bitmap, unsigned int numchars)
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <linux/swap.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include "ext4.h"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* linux/fs/ext4/crypto_key.c
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains encryption key functions for ext4
|
||||
*
|
||||
* Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
|
||||
*/
|
||||
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <uapi/linux/keyctl.h>
|
||||
|
||||
#include "ext4.h"
|
||||
#include "xattr.h"
|
||||
|
||||
static void derive_crypt_complete(struct crypto_async_request *req, int rc)
|
||||
{
|
||||
struct ext4_completion_result *ecr = req->data;
|
||||
|
||||
if (rc == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
ecr->res = rc;
|
||||
complete(&ecr->completion);
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_derive_key_aes() - Derive a key using AES-128-ECB
|
||||
* @deriving_key: Encryption key used for derivatio.
|
||||
* @source_key: Source key to which to apply derivation.
|
||||
* @derived_key: Derived key.
|
||||
*
|
||||
* Return: Zero on success; non-zero otherwise.
|
||||
*/
|
||||
static int ext4_derive_key_aes(char deriving_key[EXT4_AES_128_ECB_KEY_SIZE],
|
||||
char source_key[EXT4_AES_256_XTS_KEY_SIZE],
|
||||
char derived_key[EXT4_AES_256_XTS_KEY_SIZE])
|
||||
{
|
||||
int res = 0;
|
||||
struct ablkcipher_request *req = NULL;
|
||||
DECLARE_EXT4_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0,
|
||||
0);
|
||||
|
||||
if (IS_ERR(tfm)) {
|
||||
res = PTR_ERR(tfm);
|
||||
tfm = NULL;
|
||||
goto out;
|
||||
}
|
||||
crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
|
||||
req = ablkcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
ablkcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
derive_crypt_complete, &ecr);
|
||||
res = crypto_ablkcipher_setkey(tfm, deriving_key,
|
||||
EXT4_AES_128_ECB_KEY_SIZE);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
sg_init_one(&src_sg, source_key, EXT4_AES_256_XTS_KEY_SIZE);
|
||||
sg_init_one(&dst_sg, derived_key, EXT4_AES_256_XTS_KEY_SIZE);
|
||||
ablkcipher_request_set_crypt(req, &src_sg, &dst_sg,
|
||||
EXT4_AES_256_XTS_KEY_SIZE, NULL);
|
||||
res = crypto_ablkcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
BUG_ON(req->base.data != &ecr);
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
|
||||
out:
|
||||
if (req)
|
||||
ablkcipher_request_free(req);
|
||||
if (tfm)
|
||||
crypto_free_ablkcipher(tfm);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_generate_encryption_key() - generates an encryption key
|
||||
* @inode: The inode to generate the encryption key for.
|
||||
*/
|
||||
int ext4_generate_encryption_key(struct inode *inode)
|
||||
{
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
struct ext4_encryption_key *crypt_key = &ei->i_encryption_key;
|
||||
char full_key_descriptor[EXT4_KEY_DESC_PREFIX_SIZE +
|
||||
(EXT4_KEY_DESCRIPTOR_SIZE * 2) + 1];
|
||||
struct key *keyring_key = NULL;
|
||||
struct ext4_encryption_key *master_key;
|
||||
struct ext4_encryption_context ctx;
|
||||
struct user_key_payload *ukp;
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
&ctx, sizeof(ctx));
|
||||
|
||||
if (res != sizeof(ctx)) {
|
||||
if (res > 0)
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
res = 0;
|
||||
|
||||
if (S_ISREG(inode->i_mode))
|
||||
crypt_key->mode = ctx.contents_encryption_mode;
|
||||
else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
|
||||
crypt_key->mode = ctx.filenames_encryption_mode;
|
||||
else {
|
||||
printk(KERN_ERR "ext4 crypto: Unsupported inode type.\n");
|
||||
BUG();
|
||||
}
|
||||
crypt_key->size = ext4_encryption_key_size(crypt_key->mode);
|
||||
BUG_ON(!crypt_key->size);
|
||||
if (DUMMY_ENCRYPTION_ENABLED(sbi)) {
|
||||
memset(crypt_key->raw, 0x42, EXT4_AES_256_XTS_KEY_SIZE);
|
||||
goto out;
|
||||
}
|
||||
memcpy(full_key_descriptor, EXT4_KEY_DESC_PREFIX,
|
||||
EXT4_KEY_DESC_PREFIX_SIZE);
|
||||
sprintf(full_key_descriptor + EXT4_KEY_DESC_PREFIX_SIZE,
|
||||
"%*phN", EXT4_KEY_DESCRIPTOR_SIZE,
|
||||
ctx.master_key_descriptor);
|
||||
full_key_descriptor[EXT4_KEY_DESC_PREFIX_SIZE +
|
||||
(2 * EXT4_KEY_DESCRIPTOR_SIZE)] = '\0';
|
||||
keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
|
||||
if (IS_ERR(keyring_key)) {
|
||||
res = PTR_ERR(keyring_key);
|
||||
keyring_key = NULL;
|
||||
goto out;
|
||||
}
|
||||
BUG_ON(keyring_key->type != &key_type_logon);
|
||||
ukp = ((struct user_key_payload *)keyring_key->payload.data);
|
||||
if (ukp->datalen != sizeof(struct ext4_encryption_key)) {
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
master_key = (struct ext4_encryption_key *)ukp->data;
|
||||
BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE !=
|
||||
EXT4_KEY_DERIVATION_NONCE_SIZE);
|
||||
BUG_ON(master_key->size != EXT4_AES_256_XTS_KEY_SIZE);
|
||||
res = ext4_derive_key_aes(ctx.nonce, master_key->raw, crypt_key->raw);
|
||||
out:
|
||||
if (keyring_key)
|
||||
key_put(keyring_key);
|
||||
if (res < 0)
|
||||
crypt_key->mode = EXT4_ENCRYPTION_MODE_INVALID;
|
||||
return res;
|
||||
}
|
||||
|
||||
int ext4_has_encryption_key(struct inode *inode)
|
||||
{
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
struct ext4_encryption_key *crypt_key = &ei->i_encryption_key;
|
||||
|
||||
return (crypt_key->mode != EXT4_ENCRYPTION_MODE_INVALID);
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* linux/fs/ext4/crypto_policy.c
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains encryption policy functions for ext4
|
||||
*
|
||||
* Written by Michael Halcrow, 2015.
|
||||
*/
|
||||
|
||||
#include <linux/random.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "ext4.h"
|
||||
#include "xattr.h"
|
||||
|
||||
static int ext4_inode_has_encryption_context(struct inode *inode)
|
||||
{
|
||||
int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0);
|
||||
return (res > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* check whether the policy is consistent with the encryption context
|
||||
* for the inode
|
||||
*/
|
||||
static int ext4_is_encryption_context_consistent_with_policy(
|
||||
struct inode *inode, const struct ext4_encryption_policy *policy)
|
||||
{
|
||||
struct ext4_encryption_context ctx;
|
||||
int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
|
||||
sizeof(ctx));
|
||||
if (res != sizeof(ctx))
|
||||
return 0;
|
||||
return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
|
||||
(ctx.contents_encryption_mode ==
|
||||
policy->contents_encryption_mode) &&
|
||||
(ctx.filenames_encryption_mode ==
|
||||
policy->filenames_encryption_mode));
|
||||
}
|
||||
|
||||
static int ext4_create_encryption_context_from_policy(
|
||||
struct inode *inode, const struct ext4_encryption_policy *policy)
|
||||
{
|
||||
struct ext4_encryption_context ctx;
|
||||
int res = 0;
|
||||
|
||||
ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE);
|
||||
if (!ext4_valid_contents_enc_mode(policy->contents_encryption_mode)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Invalid contents encryption mode %d\n", __func__,
|
||||
policy->contents_encryption_mode);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!ext4_valid_filenames_enc_mode(policy->filenames_encryption_mode)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Invalid filenames encryption mode %d\n", __func__,
|
||||
policy->filenames_encryption_mode);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ctx.contents_encryption_mode = policy->contents_encryption_mode;
|
||||
ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
|
||||
BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
|
||||
get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
|
||||
|
||||
res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
|
||||
sizeof(ctx), 0);
|
||||
out:
|
||||
if (!res)
|
||||
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
return res;
|
||||
}
|
||||
|
||||
int ext4_process_policy(const struct ext4_encryption_policy *policy,
|
||||
struct inode *inode)
|
||||
{
|
||||
if (policy->version != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ext4_inode_has_encryption_context(inode)) {
|
||||
if (!ext4_empty_dir(inode))
|
||||
return -ENOTEMPTY;
|
||||
return ext4_create_encryption_context_from_policy(inode,
|
||||
policy);
|
||||
}
|
||||
|
||||
if (ext4_is_encryption_context_consistent_with_policy(inode, policy))
|
||||
return 0;
|
||||
|
||||
printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int ext4_get_policy(struct inode *inode, struct ext4_encryption_policy *policy)
|
||||
{
|
||||
struct ext4_encryption_context ctx;
|
||||
|
||||
int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
&ctx, sizeof(ctx));
|
||||
if (res != sizeof(ctx))
|
||||
return -ENOENT;
|
||||
if (ctx.format != EXT4_ENCRYPTION_CONTEXT_FORMAT_V1)
|
||||
return -EINVAL;
|
||||
policy->version = 0;
|
||||
policy->contents_encryption_mode = ctx.contents_encryption_mode;
|
||||
policy->filenames_encryption_mode = ctx.filenames_encryption_mode;
|
||||
memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ext4_is_child_context_consistent_with_parent(struct inode *parent,
|
||||
struct inode *child)
|
||||
{
|
||||
struct ext4_encryption_context parent_ctx, child_ctx;
|
||||
int res;
|
||||
|
||||
if ((parent == NULL) || (child == NULL)) {
|
||||
pr_err("parent %p child %p\n", parent, child);
|
||||
BUG_ON(1);
|
||||
}
|
||||
/* no restrictions if the parent directory is not encrypted */
|
||||
if (!ext4_encrypted_inode(parent))
|
||||
return 1;
|
||||
res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
&parent_ctx, sizeof(parent_ctx));
|
||||
if (res != sizeof(parent_ctx))
|
||||
return 0;
|
||||
/* if the child directory is not encrypted, this is always a problem */
|
||||
if (!ext4_encrypted_inode(child))
|
||||
return 0;
|
||||
res = ext4_xattr_get(child, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
&child_ctx, sizeof(child_ctx));
|
||||
if (res != sizeof(child_ctx))
|
||||
return 0;
|
||||
return (memcmp(parent_ctx.master_key_descriptor,
|
||||
child_ctx.master_key_descriptor,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
|
||||
(parent_ctx.contents_encryption_mode ==
|
||||
child_ctx.contents_encryption_mode) &&
|
||||
(parent_ctx.filenames_encryption_mode ==
|
||||
child_ctx.filenames_encryption_mode));
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_inherit_context() - Sets a child context from its parent
|
||||
* @parent: Parent inode from which the context is inherited.
|
||||
* @child: Child inode that inherits the context from @parent.
|
||||
*
|
||||
* Return: Zero on success, non-zero otherwise
|
||||
*/
|
||||
int ext4_inherit_context(struct inode *parent, struct inode *child)
|
||||
{
|
||||
struct ext4_encryption_context ctx;
|
||||
int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
&ctx, sizeof(ctx));
|
||||
|
||||
if (res != sizeof(ctx)) {
|
||||
if (DUMMY_ENCRYPTION_ENABLED(EXT4_SB(parent->i_sb))) {
|
||||
ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
ctx.contents_encryption_mode =
|
||||
EXT4_ENCRYPTION_MODE_AES_256_XTS;
|
||||
ctx.filenames_encryption_mode =
|
||||
EXT4_ENCRYPTION_MODE_AES_256_CTS;
|
||||
memset(ctx.master_key_descriptor, 0x42,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE);
|
||||
res = 0;
|
||||
} else {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
|
||||
res = ext4_xattr_set(child, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
|
||||
sizeof(ctx), 0);
|
||||
out:
|
||||
if (!res)
|
||||
ext4_set_inode_flag(child, EXT4_INODE_ENCRYPT);
|
||||
return res;
|
||||
}
|
||||
+59
-22
@@ -22,10 +22,8 @@
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/jbd2.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include "ext4.h"
|
||||
#include "xattr.h"
|
||||
|
||||
@@ -110,7 +108,10 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
||||
int err;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh = NULL;
|
||||
int dir_has_error = 0;
|
||||
struct ext4_fname_crypto_ctx *enc_ctx = NULL;
|
||||
struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
|
||||
|
||||
if (is_dx_dir(inode)) {
|
||||
err = ext4_dx_readdir(file, ctx);
|
||||
@@ -127,17 +128,28 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
||||
|
||||
if (ext4_has_inline_data(inode)) {
|
||||
int has_inline_data = 1;
|
||||
int ret = ext4_read_inline_dir(file, ctx,
|
||||
err = ext4_read_inline_dir(file, ctx,
|
||||
&has_inline_data);
|
||||
if (has_inline_data)
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
enc_ctx = ext4_get_fname_crypto_ctx(inode, EXT4_NAME_LEN);
|
||||
if (IS_ERR(enc_ctx))
|
||||
return PTR_ERR(enc_ctx);
|
||||
if (enc_ctx) {
|
||||
err = ext4_fname_crypto_alloc_buffer(enc_ctx, EXT4_NAME_LEN,
|
||||
&fname_crypto_str);
|
||||
if (err < 0) {
|
||||
ext4_put_fname_crypto_ctx(&enc_ctx);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
offset = ctx->pos & (sb->s_blocksize - 1);
|
||||
|
||||
while (ctx->pos < inode->i_size) {
|
||||
struct ext4_map_blocks map;
|
||||
struct buffer_head *bh = NULL;
|
||||
|
||||
map.m_lblk = ctx->pos >> EXT4_BLOCK_SIZE_BITS(sb);
|
||||
map.m_len = 1;
|
||||
@@ -180,6 +192,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
||||
(unsigned long long)ctx->pos);
|
||||
ctx->pos += sb->s_blocksize - offset;
|
||||
brelse(bh);
|
||||
bh = NULL;
|
||||
continue;
|
||||
}
|
||||
set_buffer_verified(bh);
|
||||
@@ -226,25 +239,44 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
||||
offset += ext4_rec_len_from_disk(de->rec_len,
|
||||
sb->s_blocksize);
|
||||
if (le32_to_cpu(de->inode)) {
|
||||
if (!dir_emit(ctx, de->name,
|
||||
de->name_len,
|
||||
le32_to_cpu(de->inode),
|
||||
get_dtype(sb, de->file_type))) {
|
||||
brelse(bh);
|
||||
return 0;
|
||||
if (enc_ctx == NULL) {
|
||||
/* Directory is not encrypted */
|
||||
if (!dir_emit(ctx, de->name,
|
||||
de->name_len,
|
||||
le32_to_cpu(de->inode),
|
||||
get_dtype(sb, de->file_type)))
|
||||
goto done;
|
||||
} else {
|
||||
/* Directory is encrypted */
|
||||
err = ext4_fname_disk_to_usr(enc_ctx,
|
||||
de, &fname_crypto_str);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
if (!dir_emit(ctx,
|
||||
fname_crypto_str.name, err,
|
||||
le32_to_cpu(de->inode),
|
||||
get_dtype(sb, de->file_type)))
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
ctx->pos += ext4_rec_len_from_disk(de->rec_len,
|
||||
sb->s_blocksize);
|
||||
}
|
||||
offset = 0;
|
||||
if ((ctx->pos < inode->i_size) && !dir_relax(inode))
|
||||
goto done;
|
||||
brelse(bh);
|
||||
if (ctx->pos < inode->i_size) {
|
||||
if (!dir_relax(inode))
|
||||
return 0;
|
||||
}
|
||||
bh = NULL;
|
||||
offset = 0;
|
||||
}
|
||||
return 0;
|
||||
done:
|
||||
err = 0;
|
||||
errout:
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
ext4_put_fname_crypto_ctx(&enc_ctx);
|
||||
ext4_fname_crypto_free_buffer(&fname_crypto_str);
|
||||
#endif
|
||||
brelse(bh);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int is_32bit_api(void)
|
||||
@@ -384,10 +416,15 @@ void ext4_htree_free_dir_info(struct dir_private_info *p)
|
||||
|
||||
/*
|
||||
* Given a directory entry, enter it into the fname rb tree.
|
||||
*
|
||||
* When filename encryption is enabled, the dirent will hold the
|
||||
* encrypted filename, while the htree will hold decrypted filename.
|
||||
* The decrypted filename is passed in via ent_name. parameter.
|
||||
*/
|
||||
int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
|
||||
__u32 minor_hash,
|
||||
struct ext4_dir_entry_2 *dirent)
|
||||
struct ext4_dir_entry_2 *dirent,
|
||||
struct ext4_str *ent_name)
|
||||
{
|
||||
struct rb_node **p, *parent = NULL;
|
||||
struct fname *fname, *new_fn;
|
||||
@@ -398,17 +435,17 @@ int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
|
||||
p = &info->root.rb_node;
|
||||
|
||||
/* Create and allocate the fname structure */
|
||||
len = sizeof(struct fname) + dirent->name_len + 1;
|
||||
len = sizeof(struct fname) + ent_name->len + 1;
|
||||
new_fn = kzalloc(len, GFP_KERNEL);
|
||||
if (!new_fn)
|
||||
return -ENOMEM;
|
||||
new_fn->hash = hash;
|
||||
new_fn->minor_hash = minor_hash;
|
||||
new_fn->inode = le32_to_cpu(dirent->inode);
|
||||
new_fn->name_len = dirent->name_len;
|
||||
new_fn->name_len = ent_name->len;
|
||||
new_fn->file_type = dirent->file_type;
|
||||
memcpy(new_fn->name, dirent->name, dirent->name_len);
|
||||
new_fn->name[dirent->name_len] = 0;
|
||||
memcpy(new_fn->name, ent_name->name, ent_name->len);
|
||||
new_fn->name[ent_name->len] = 0;
|
||||
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
|
||||
+159
-10
@@ -422,7 +422,7 @@ enum {
|
||||
EXT4_INODE_DIRTY = 8,
|
||||
EXT4_INODE_COMPRBLK = 9, /* One or more compressed clusters */
|
||||
EXT4_INODE_NOCOMPR = 10, /* Don't compress */
|
||||
EXT4_INODE_ENCRYPT = 11, /* Compression error */
|
||||
EXT4_INODE_ENCRYPT = 11, /* Encrypted file */
|
||||
/* End compression flags --- maybe not all used */
|
||||
EXT4_INODE_INDEX = 12, /* hash-indexed directory */
|
||||
EXT4_INODE_IMAGIC = 13, /* AFS directory */
|
||||
@@ -582,6 +582,15 @@ enum {
|
||||
#define EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER 0x0010
|
||||
#define EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER 0x0020
|
||||
|
||||
/* Encryption algorithms */
|
||||
#define EXT4_ENCRYPTION_MODE_INVALID 0
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_GCM 2
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
|
||||
|
||||
#include "ext4_crypto.h"
|
||||
|
||||
/*
|
||||
* ioctl commands
|
||||
*/
|
||||
@@ -603,6 +612,9 @@ enum {
|
||||
#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
|
||||
#define EXT4_IOC_SWAP_BOOT _IO('f', 17)
|
||||
#define EXT4_IOC_PRECACHE_EXTENTS _IO('f', 18)
|
||||
#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
|
||||
#define EXT4_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
|
||||
#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
|
||||
|
||||
#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
|
||||
/*
|
||||
@@ -939,6 +951,11 @@ struct ext4_inode_info {
|
||||
|
||||
/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
|
||||
__u32 i_csum_seed;
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
/* Encryption params */
|
||||
struct ext4_encryption_key i_encryption_key;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1142,7 +1159,8 @@ struct ext4_super_block {
|
||||
__le32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
|
||||
__u8 s_log_groups_per_flex; /* FLEX_BG group size */
|
||||
__u8 s_checksum_type; /* metadata checksum algorithm used */
|
||||
__le16 s_reserved_pad;
|
||||
__u8 s_encryption_level; /* versioning level for encryption */
|
||||
__u8 s_reserved_pad; /* Padding to next 32bits */
|
||||
__le64 s_kbytes_written; /* nr of lifetime kilobytes written */
|
||||
__le32 s_snapshot_inum; /* Inode number of active snapshot */
|
||||
__le32 s_snapshot_id; /* sequential ID of active snapshot */
|
||||
@@ -1169,7 +1187,9 @@ struct ext4_super_block {
|
||||
__le32 s_overhead_clusters; /* overhead blocks/clusters in fs */
|
||||
__le32 s_backup_bgs[2]; /* groups with sparse_super2 SBs */
|
||||
__u8 s_encrypt_algos[4]; /* Encryption algorithms in use */
|
||||
__le32 s_reserved[105]; /* Padding to the end of the block */
|
||||
__u8 s_encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
|
||||
__le32 s_lpf_ino; /* Location of the lost+found inode */
|
||||
__le32 s_reserved[100]; /* Padding to the end of the block */
|
||||
__le32 s_checksum; /* crc32c(superblock) */
|
||||
};
|
||||
|
||||
@@ -1180,8 +1200,16 @@ struct ext4_super_block {
|
||||
/*
|
||||
* run-time mount flags
|
||||
*/
|
||||
#define EXT4_MF_MNTDIR_SAMPLED 0x0001
|
||||
#define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */
|
||||
#define EXT4_MF_MNTDIR_SAMPLED 0x0001
|
||||
#define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */
|
||||
#define EXT4_MF_TEST_DUMMY_ENCRYPTION 0x0004
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
#define DUMMY_ENCRYPTION_ENABLED(sbi) (unlikely((sbi)->s_mount_flags & \
|
||||
EXT4_MF_TEST_DUMMY_ENCRYPTION))
|
||||
#else
|
||||
#define DUMMY_ENCRYPTION_ENABLED(sbi) (0)
|
||||
#endif
|
||||
|
||||
/* Number of quota types we support */
|
||||
#define EXT4_MAXQUOTAS 2
|
||||
@@ -1351,6 +1379,12 @@ struct ext4_sb_info {
|
||||
struct ratelimit_state s_err_ratelimit_state;
|
||||
struct ratelimit_state s_warning_ratelimit_state;
|
||||
struct ratelimit_state s_msg_ratelimit_state;
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
/* Encryption */
|
||||
uint32_t s_file_encryption_mode;
|
||||
uint32_t s_dir_encryption_mode;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
|
||||
@@ -1466,6 +1500,18 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
|
||||
#define EXT4_SB(sb) (sb)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Returns true if the inode is inode is encrypted
|
||||
*/
|
||||
static inline int ext4_encrypted_inode(struct inode *inode)
|
||||
{
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
return ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime
|
||||
|
||||
/*
|
||||
@@ -1575,8 +1621,9 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
|
||||
EXT4_FEATURE_INCOMPAT_EXTENTS| \
|
||||
EXT4_FEATURE_INCOMPAT_64BIT| \
|
||||
EXT4_FEATURE_INCOMPAT_FLEX_BG| \
|
||||
EXT4_FEATURE_INCOMPAT_MMP | \
|
||||
EXT4_FEATURE_INCOMPAT_INLINE_DATA)
|
||||
EXT4_FEATURE_INCOMPAT_MMP | \
|
||||
EXT4_FEATURE_INCOMPAT_INLINE_DATA | \
|
||||
EXT4_FEATURE_INCOMPAT_ENCRYPT)
|
||||
#define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
|
||||
EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \
|
||||
EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \
|
||||
@@ -2001,6 +2048,99 @@ extern unsigned ext4_free_clusters_after_init(struct super_block *sb,
|
||||
struct ext4_group_desc *gdp);
|
||||
ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
|
||||
|
||||
/* crypto_policy.c */
|
||||
int ext4_is_child_context_consistent_with_parent(struct inode *parent,
|
||||
struct inode *child);
|
||||
int ext4_inherit_context(struct inode *parent, struct inode *child);
|
||||
void ext4_to_hex(char *dst, char *src, size_t src_size);
|
||||
int ext4_process_policy(const struct ext4_encryption_policy *policy,
|
||||
struct inode *inode);
|
||||
int ext4_get_policy(struct inode *inode,
|
||||
struct ext4_encryption_policy *policy);
|
||||
|
||||
/* crypto.c */
|
||||
bool ext4_valid_contents_enc_mode(uint32_t mode);
|
||||
uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size);
|
||||
extern struct workqueue_struct *ext4_read_workqueue;
|
||||
struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode);
|
||||
void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx);
|
||||
void ext4_restore_control_page(struct page *data_page);
|
||||
struct page *ext4_encrypt(struct inode *inode,
|
||||
struct page *plaintext_page);
|
||||
int ext4_decrypt(struct ext4_crypto_ctx *ctx, struct page *page);
|
||||
int ext4_decrypt_one(struct inode *inode, struct page *page);
|
||||
int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex);
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
int ext4_init_crypto(void);
|
||||
void ext4_exit_crypto(void);
|
||||
static inline int ext4_sb_has_crypto(struct super_block *sb)
|
||||
{
|
||||
return EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT);
|
||||
}
|
||||
#else
|
||||
static inline int ext4_init_crypto(void) { return 0; }
|
||||
static inline void ext4_exit_crypto(void) { }
|
||||
static inline int ext4_sb_has_crypto(struct super_block *sb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* crypto_fname.c */
|
||||
bool ext4_valid_filenames_enc_mode(uint32_t mode);
|
||||
u32 ext4_fname_crypto_round_up(u32 size, u32 blksize);
|
||||
int ext4_fname_crypto_alloc_buffer(struct ext4_fname_crypto_ctx *ctx,
|
||||
u32 ilen, struct ext4_str *crypto_str);
|
||||
int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct ext4_str *iname,
|
||||
struct ext4_str *oname);
|
||||
int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct ext4_dir_entry_2 *de,
|
||||
struct ext4_str *oname);
|
||||
int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct qstr *iname,
|
||||
struct ext4_str *oname);
|
||||
int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct qstr *iname,
|
||||
struct dx_hash_info *hinfo);
|
||||
int ext4_fname_disk_to_hash(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct ext4_dir_entry_2 *de,
|
||||
struct dx_hash_info *hinfo);
|
||||
int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx,
|
||||
u32 namelen);
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx);
|
||||
struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(struct inode *inode,
|
||||
u32 max_len);
|
||||
void ext4_fname_crypto_free_buffer(struct ext4_str *crypto_str);
|
||||
#else
|
||||
static inline
|
||||
void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx) { }
|
||||
static inline
|
||||
struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(struct inode *inode,
|
||||
u32 max_len)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void ext4_fname_crypto_free_buffer(struct ext4_str *p) { }
|
||||
#endif
|
||||
|
||||
|
||||
/* crypto_key.c */
|
||||
int ext4_generate_encryption_key(struct inode *inode);
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
int ext4_has_encryption_key(struct inode *inode);
|
||||
#else
|
||||
static inline int ext4_has_encryption_key(struct inode *inode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* dir.c */
|
||||
extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
|
||||
struct file *,
|
||||
@@ -2011,17 +2151,20 @@ extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
|
||||
unlikely(__ext4_check_dir_entry(__func__, __LINE__, (dir), (filp), \
|
||||
(de), (bh), (buf), (size), (offset)))
|
||||
extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
|
||||
__u32 minor_hash,
|
||||
struct ext4_dir_entry_2 *dirent);
|
||||
__u32 minor_hash,
|
||||
struct ext4_dir_entry_2 *dirent,
|
||||
struct ext4_str *ent_name);
|
||||
extern void ext4_htree_free_dir_info(struct dir_private_info *p);
|
||||
extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
||||
struct buffer_head *bh,
|
||||
void *buf, int buf_size,
|
||||
const char *name, int namelen,
|
||||
struct ext4_dir_entry_2 **dest_de);
|
||||
void ext4_insert_dentry(struct inode *inode,
|
||||
int ext4_insert_dentry(struct inode *dir,
|
||||
struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int buf_size,
|
||||
const struct qstr *iname,
|
||||
const char *name, int namelen);
|
||||
static inline void ext4_update_dx_flag(struct inode *inode)
|
||||
{
|
||||
@@ -2099,6 +2242,7 @@ extern int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
|
||||
extern int ext4_trim_fs(struct super_block *, struct fstrim_range *);
|
||||
|
||||
/* inode.c */
|
||||
int ext4_inode_is_fast_symlink(struct inode *inode);
|
||||
struct buffer_head *ext4_getblk(handle_t *, struct inode *, ext4_lblk_t, int);
|
||||
struct buffer_head *ext4_bread(handle_t *, struct inode *, ext4_lblk_t, int);
|
||||
int ext4_get_block_write(struct inode *inode, sector_t iblock,
|
||||
@@ -2189,6 +2333,7 @@ extern int ext4_generic_delete_entry(handle_t *handle,
|
||||
void *entry_buf,
|
||||
int buf_size,
|
||||
int csum_size);
|
||||
extern int ext4_empty_dir(struct inode *inode);
|
||||
|
||||
/* resize.c */
|
||||
extern int ext4_group_add(struct super_block *sb,
|
||||
@@ -2698,6 +2843,10 @@ static inline void ext4_set_de_type(struct super_block *sb,
|
||||
de->file_type = ext4_type_by_mode[(mode & S_IFMT)>>S_SHIFT];
|
||||
}
|
||||
|
||||
/* readpages.c */
|
||||
extern int ext4_mpage_readpages(struct address_space *mapping,
|
||||
struct list_head *pages, struct page *page,
|
||||
unsigned nr_pages);
|
||||
|
||||
/* symlink.c */
|
||||
extern const struct inode_operations ext4_symlink_inode_operations;
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* linux/fs/ext4/ext4_crypto.h
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains encryption header content for ext4
|
||||
*
|
||||
* Written by Michael Halcrow, 2015.
|
||||
*/
|
||||
|
||||
#ifndef _EXT4_CRYPTO_H
|
||||
#define _EXT4_CRYPTO_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
#define EXT4_KEY_DESCRIPTOR_SIZE 8
|
||||
|
||||
/* Policy provided via an ioctl on the topmost directory */
|
||||
struct ext4_encryption_policy {
|
||||
char version;
|
||||
char contents_encryption_mode;
|
||||
char filenames_encryption_mode;
|
||||
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V1 1
|
||||
#define EXT4_KEY_DERIVATION_NONCE_SIZE 16
|
||||
|
||||
/**
|
||||
* Encryption context for inode
|
||||
*
|
||||
* Protector format:
|
||||
* 1 byte: Protector format (1 = this version)
|
||||
* 1 byte: File contents encryption mode
|
||||
* 1 byte: File names encryption mode
|
||||
* 1 byte: Reserved
|
||||
* 8 bytes: Master Key descriptor
|
||||
* 16 bytes: Encryption Key derivation nonce
|
||||
*/
|
||||
struct ext4_encryption_context {
|
||||
char format;
|
||||
char contents_encryption_mode;
|
||||
char filenames_encryption_mode;
|
||||
char reserved;
|
||||
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
|
||||
char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* Encryption parameters */
|
||||
#define EXT4_XTS_TWEAK_SIZE 16
|
||||
#define EXT4_AES_128_ECB_KEY_SIZE 16
|
||||
#define EXT4_AES_256_GCM_KEY_SIZE 32
|
||||
#define EXT4_AES_256_CBC_KEY_SIZE 32
|
||||
#define EXT4_AES_256_CTS_KEY_SIZE 32
|
||||
#define EXT4_AES_256_XTS_KEY_SIZE 64
|
||||
#define EXT4_MAX_KEY_SIZE 64
|
||||
|
||||
#define EXT4_KEY_DESC_PREFIX "ext4:"
|
||||
#define EXT4_KEY_DESC_PREFIX_SIZE 5
|
||||
|
||||
struct ext4_encryption_key {
|
||||
uint32_t mode;
|
||||
char raw[EXT4_MAX_KEY_SIZE];
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
#define EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
|
||||
#define EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL 0x00000002
|
||||
|
||||
struct ext4_crypto_ctx {
|
||||
struct crypto_tfm *tfm; /* Crypto API context */
|
||||
struct page *bounce_page; /* Ciphertext page on write path */
|
||||
struct page *control_page; /* Original page on write path */
|
||||
struct bio *bio; /* The bio for this context */
|
||||
struct work_struct work; /* Work queue for read complete path */
|
||||
struct list_head free_list; /* Free list */
|
||||
int flags; /* Flags */
|
||||
int mode; /* Encryption mode for tfm */
|
||||
};
|
||||
|
||||
struct ext4_completion_result {
|
||||
struct completion completion;
|
||||
int res;
|
||||
};
|
||||
|
||||
#define DECLARE_EXT4_COMPLETION_RESULT(ecr) \
|
||||
struct ext4_completion_result ecr = { \
|
||||
COMPLETION_INITIALIZER((ecr).completion), 0 }
|
||||
|
||||
static inline int ext4_encryption_key_size(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_XTS:
|
||||
return EXT4_AES_256_XTS_KEY_SIZE;
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_GCM:
|
||||
return EXT4_AES_256_GCM_KEY_SIZE;
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_CBC:
|
||||
return EXT4_AES_256_CBC_KEY_SIZE;
|
||||
case EXT4_ENCRYPTION_MODE_AES_256_CTS:
|
||||
return EXT4_AES_256_CTS_KEY_SIZE;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define EXT4_FNAME_NUM_SCATTER_ENTRIES 4
|
||||
#define EXT4_CRYPTO_BLOCK_SIZE 16
|
||||
#define EXT4_FNAME_CRYPTO_DIGEST_SIZE 32
|
||||
|
||||
struct ext4_str {
|
||||
unsigned char *name;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
struct ext4_fname_crypto_ctx {
|
||||
u32 lim;
|
||||
char tmp_buf[EXT4_CRYPTO_BLOCK_SIZE];
|
||||
struct crypto_ablkcipher *ctfm;
|
||||
struct crypto_hash *htfm;
|
||||
struct page *workpage;
|
||||
struct ext4_encryption_key key;
|
||||
unsigned has_valid_key : 1;
|
||||
unsigned ctfm_key_is_ready : 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* For encrypted symlinks, the ciphertext length is stored at the beginning
|
||||
* of the string in little-endian format.
|
||||
*/
|
||||
struct ext4_encrypted_symlink_data {
|
||||
__le16 len;
|
||||
char encrypted_path[1];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* This function is used to calculate the disk space required to
|
||||
* store a filename of length l in encrypted symlink format.
|
||||
*/
|
||||
static inline u32 encrypted_symlink_data_len(u32 l)
|
||||
{
|
||||
if (l < EXT4_CRYPTO_BLOCK_SIZE)
|
||||
l = EXT4_CRYPTO_BLOCK_SIZE;
|
||||
return (l + sizeof(struct ext4_encrypted_symlink_data) - 1);
|
||||
}
|
||||
|
||||
#endif /* _EXT4_CRYPTO_H */
|
||||
+37
-44
@@ -1717,12 +1717,6 @@ ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1,
|
||||
{
|
||||
unsigned short ext1_ee_len, ext2_ee_len;
|
||||
|
||||
/*
|
||||
* Make sure that both extents are initialized. We don't merge
|
||||
* unwritten extents so that we can be sure that end_io code has
|
||||
* the extent that was written properly split out and conversion to
|
||||
* initialized is trivial.
|
||||
*/
|
||||
if (ext4_ext_is_unwritten(ex1) != ext4_ext_is_unwritten(ex2))
|
||||
return 0;
|
||||
|
||||
@@ -3128,6 +3122,9 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
|
||||
ee_len = ext4_ext_get_actual_len(ex);
|
||||
ee_pblock = ext4_ext_pblock(ex);
|
||||
|
||||
if (ext4_encrypted_inode(inode))
|
||||
return ext4_encrypted_zeroout(inode, ex);
|
||||
|
||||
ret = sb_issue_zeroout(inode->i_sb, ee_pblock, ee_len, GFP_NOFS);
|
||||
if (ret > 0)
|
||||
ret = 0;
|
||||
@@ -4535,19 +4532,7 @@ got_allocated_blocks:
|
||||
*/
|
||||
reserved_clusters = get_reserved_cluster_alloc(inode,
|
||||
map->m_lblk, allocated);
|
||||
if (map_from_cluster) {
|
||||
if (reserved_clusters) {
|
||||
/*
|
||||
* We have clusters reserved for this range.
|
||||
* But since we are not doing actual allocation
|
||||
* and are simply using blocks from previously
|
||||
* allocated cluster, we should release the
|
||||
* reservation and not claim quota.
|
||||
*/
|
||||
ext4_da_update_reserve_space(inode,
|
||||
reserved_clusters, 0);
|
||||
}
|
||||
} else {
|
||||
if (!map_from_cluster) {
|
||||
BUG_ON(allocated_clusters < reserved_clusters);
|
||||
if (reserved_clusters < allocated_clusters) {
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
@@ -4803,12 +4788,6 @@ static long ext4_zero_range(struct file *file, loff_t offset,
|
||||
else
|
||||
max_blocks -= lblk;
|
||||
|
||||
flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT |
|
||||
EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
|
||||
EXT4_EX_NOCACHE;
|
||||
if (mode & FALLOC_FL_KEEP_SIZE)
|
||||
flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
/*
|
||||
@@ -4825,15 +4804,28 @@ static long ext4_zero_range(struct file *file, loff_t offset,
|
||||
ret = inode_newsize_ok(inode, new_size);
|
||||
if (ret)
|
||||
goto out_mutex;
|
||||
/*
|
||||
* If we have a partial block after EOF we have to allocate
|
||||
* the entire block.
|
||||
*/
|
||||
if (partial_end)
|
||||
max_blocks += 1;
|
||||
}
|
||||
|
||||
flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT;
|
||||
if (mode & FALLOC_FL_KEEP_SIZE)
|
||||
flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
|
||||
|
||||
/* Preallocate the range including the unaligned edges */
|
||||
if (partial_begin || partial_end) {
|
||||
ret = ext4_alloc_file_blocks(file,
|
||||
round_down(offset, 1 << blkbits) >> blkbits,
|
||||
(round_up((offset + len), 1 << blkbits) -
|
||||
round_down(offset, 1 << blkbits)) >> blkbits,
|
||||
new_size, flags, mode);
|
||||
if (ret)
|
||||
goto out_mutex;
|
||||
|
||||
}
|
||||
|
||||
/* Zero range excluding the unaligned edges */
|
||||
if (max_blocks > 0) {
|
||||
flags |= (EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
|
||||
EXT4_EX_NOCACHE);
|
||||
|
||||
/* Now release the pages and zero block aligned part of pages*/
|
||||
truncate_pagecache_range(inode, start, end - 1);
|
||||
@@ -4847,19 +4839,6 @@ static long ext4_zero_range(struct file *file, loff_t offset,
|
||||
flags, mode);
|
||||
if (ret)
|
||||
goto out_dio;
|
||||
/*
|
||||
* Remove entire range from the extent status tree.
|
||||
*
|
||||
* ext4_es_remove_extent(inode, lblk, max_blocks) is
|
||||
* NOT sufficient. I'm not sure why this is the case,
|
||||
* but let's be conservative and remove the extent
|
||||
* status tree for the entire inode. There should be
|
||||
* no outstanding delalloc extents thanks to the
|
||||
* filemap_write_and_wait_range() call above.
|
||||
*/
|
||||
ret = ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
|
||||
if (ret)
|
||||
goto out_dio;
|
||||
}
|
||||
if (!partial_begin && !partial_end)
|
||||
goto out_dio;
|
||||
@@ -4922,6 +4901,20 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
|
||||
ext4_lblk_t lblk;
|
||||
unsigned int blkbits = inode->i_blkbits;
|
||||
|
||||
/*
|
||||
* Encrypted inodes can't handle collapse range or insert
|
||||
* range since we would need to re-encrypt blocks with a
|
||||
* different IV or XTS tweak (which are based on the logical
|
||||
* block number).
|
||||
*
|
||||
* XXX It's not clear why zero range isn't working, but we'll
|
||||
* leave it disabled for encrypted inodes for now. This is a
|
||||
* bug we should fix....
|
||||
*/
|
||||
if (ext4_encrypted_inode(inode) &&
|
||||
(mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Return error if mode is not supported */
|
||||
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
|
||||
FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE))
|
||||
|
||||
@@ -9,12 +9,10 @@
|
||||
*
|
||||
* Ext4 extents status tree core functions.
|
||||
*/
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/list_sort.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include "ext4.h"
|
||||
#include "extents_status.h"
|
||||
|
||||
#include <trace/events/ext4.h>
|
||||
|
||||
|
||||
+16
-3
@@ -20,7 +20,6 @@
|
||||
|
||||
#include <linux/time.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/jbd2.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/path.h>
|
||||
#include <linux/quotaops.h>
|
||||
@@ -221,6 +220,13 @@ static const struct vm_operations_struct ext4_file_vm_ops = {
|
||||
|
||||
static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
|
||||
if (ext4_encrypted_inode(inode)) {
|
||||
int err = ext4_generate_encryption_key(inode);
|
||||
if (err)
|
||||
return 0;
|
||||
}
|
||||
file_accessed(file);
|
||||
if (IS_DAX(file_inode(file))) {
|
||||
vma->vm_ops = &ext4_dax_vm_ops;
|
||||
@@ -238,6 +244,7 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
|
||||
struct vfsmount *mnt = filp->f_path.mnt;
|
||||
struct path path;
|
||||
char buf[64], *cp;
|
||||
int ret;
|
||||
|
||||
if (unlikely(!(sbi->s_mount_flags & EXT4_MF_MNTDIR_SAMPLED) &&
|
||||
!(sb->s_flags & MS_RDONLY))) {
|
||||
@@ -276,11 +283,17 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
|
||||
* writing and the journal is present
|
||||
*/
|
||||
if (filp->f_mode & FMODE_WRITE) {
|
||||
int ret = ext4_inode_attach_jinode(inode);
|
||||
ret = ext4_inode_attach_jinode(inode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return dquot_file_open(inode, filp);
|
||||
ret = dquot_file_open(inode, filp);
|
||||
if (!ret && ext4_encrypted_inode(inode)) {
|
||||
ret = ext4_generate_encryption_key(inode);
|
||||
if (ret)
|
||||
ret = -EACCES;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/jbd2.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
#include "ext4.h"
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/jbd2.h>
|
||||
#include <linux/cryptohash.h>
|
||||
#include "ext4.h"
|
||||
|
||||
|
||||
+25
-3
@@ -14,7 +14,6 @@
|
||||
|
||||
#include <linux/time.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/jbd2.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/quotaops.h>
|
||||
@@ -997,6 +996,12 @@ got:
|
||||
ei->i_block_group = group;
|
||||
ei->i_last_alloc_group = ~0;
|
||||
|
||||
/* If the directory encrypted, then we should encrypt the inode. */
|
||||
if ((S_ISDIR(mode) || S_ISREG(mode) || S_ISLNK(mode)) &&
|
||||
(ext4_encrypted_inode(dir) ||
|
||||
DUMMY_ENCRYPTION_ENABLED(sbi)))
|
||||
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
|
||||
ext4_set_inode_flags(inode);
|
||||
if (IS_DIRSYNC(inode))
|
||||
ext4_handle_sync(handle);
|
||||
@@ -1029,11 +1034,28 @@ got:
|
||||
ext4_set_inode_state(inode, EXT4_STATE_NEW);
|
||||
|
||||
ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
if ((sbi->s_file_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID) &&
|
||||
(sbi->s_dir_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID)) {
|
||||
ei->i_inline_off = 0;
|
||||
if (EXT4_HAS_INCOMPAT_FEATURE(sb,
|
||||
EXT4_FEATURE_INCOMPAT_INLINE_DATA))
|
||||
ext4_set_inode_state(inode,
|
||||
EXT4_STATE_MAY_INLINE_DATA);
|
||||
} else {
|
||||
/* Inline data and encryption are incompatible
|
||||
* We turn off inline data since encryption is enabled */
|
||||
ei->i_inline_off = 1;
|
||||
if (EXT4_HAS_INCOMPAT_FEATURE(sb,
|
||||
EXT4_FEATURE_INCOMPAT_INLINE_DATA))
|
||||
ext4_clear_inode_state(inode,
|
||||
EXT4_STATE_MAY_INLINE_DATA);
|
||||
}
|
||||
#else
|
||||
ei->i_inline_off = 0;
|
||||
if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_INLINE_DATA))
|
||||
ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
|
||||
|
||||
#endif
|
||||
ret = inode;
|
||||
err = dquot_alloc_inode(inode);
|
||||
if (err)
|
||||
|
||||
+11
-5
@@ -11,11 +11,13 @@
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/fiemap.h>
|
||||
|
||||
#include "ext4_jbd2.h"
|
||||
#include "ext4.h"
|
||||
#include "xattr.h"
|
||||
#include "truncate.h"
|
||||
#include <linux/fiemap.h>
|
||||
|
||||
#define EXT4_XATTR_SYSTEM_DATA "data"
|
||||
#define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS))
|
||||
@@ -972,7 +974,7 @@ void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh,
|
||||
offset = 0;
|
||||
while ((void *)de < dlimit) {
|
||||
de_len = ext4_rec_len_from_disk(de->rec_len, inline_size);
|
||||
trace_printk("de: off %u rlen %u name %*.s nlen %u ino %u\n",
|
||||
trace_printk("de: off %u rlen %u name %.*s nlen %u ino %u\n",
|
||||
offset, de_len, de->name_len, de->name,
|
||||
de->name_len, le32_to_cpu(de->inode));
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh,
|
||||
@@ -1014,7 +1016,8 @@ static int ext4_add_dirent_to_inline(handle_t *handle,
|
||||
err = ext4_journal_get_write_access(handle, iloc->bh);
|
||||
if (err)
|
||||
return err;
|
||||
ext4_insert_dentry(inode, de, inline_size, name, namelen);
|
||||
ext4_insert_dentry(dir, inode, de, inline_size, &dentry->d_name,
|
||||
name, namelen);
|
||||
|
||||
ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size);
|
||||
|
||||
@@ -1327,6 +1330,7 @@ int htree_inlinedir_to_tree(struct file *dir_file,
|
||||
struct ext4_iloc iloc;
|
||||
void *dir_buf = NULL;
|
||||
struct ext4_dir_entry_2 fake;
|
||||
struct ext4_str tmp_str;
|
||||
|
||||
ret = ext4_get_inode_loc(inode, &iloc);
|
||||
if (ret)
|
||||
@@ -1398,8 +1402,10 @@ int htree_inlinedir_to_tree(struct file *dir_file,
|
||||
continue;
|
||||
if (de->inode == 0)
|
||||
continue;
|
||||
err = ext4_htree_store_dirent(dir_file,
|
||||
hinfo->hash, hinfo->minor_hash, de);
|
||||
tmp_str.name = de->name;
|
||||
tmp_str.len = de->name_len;
|
||||
err = ext4_htree_store_dirent(dir_file, hinfo->hash,
|
||||
hinfo->minor_hash, de, &tmp_str);
|
||||
if (err) {
|
||||
count = err;
|
||||
goto out;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user