Commit 396d1099 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Pull ext4 updates from Ted Ts'o:
 "The major change this cycle is deleting ext4's copy of the file system
  encryption code and switching things over to using the copies in
  fs/crypto.  I've updated the MAINTAINERS file to add an entry for
  fs/crypto listing Jaeguk Kim and myself as the maintainers.

  There are also a number of bug fixes, most notably for some problems
  found by American Fuzzy Lop (AFL) courtesy of Vegard Nossum.  Also
  fixed is a writeback deadlock detected by generic/130, and some
  potential races in the metadata checksum code"

* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (21 commits)
  ext4: verify extent header depth
  ext4: short-cut orphan cleanup on error
  ext4: fix reference counting bug on block allocation error
  MAINTAINRES: fs-crypto maintainers update
  ext4 crypto: migrate into vfs's crypto engine
  ext2: fix filesystem deadlock while reading corrupted xattr block
  ext4: fix project quota accounting without quota limits enabled
  ext4: validate s_reserved_gdt_blocks on mount
  ext4: remove unused page_idx
  ext4: don't call ext4_should_journal_data() on the journal inode
  ext4: Fix WARN_ON_ONCE in ext4_commit_super()
  ext4: fix deadlock during page writeback
  ext4: correct error value of function verifying dx checksum
  ext4: avoid modifying checksum fields directly during checksum verification
  ext4: check for extents that wrap around
  jbd2: make journal y2038 safe
  jbd2: track more dependencies on transaction commit
  jbd2: move lockdep tracking to journal_s
  jbd2: move lockdep instrumentation for jbd2 handles
  ext4: respect the nobarrier mount option in nojournal mode
  ...
parents 59ebc44e 7bc94916
...@@ -4942,6 +4942,13 @@ F: Documentation/filesystems/caching/ ...@@ -4942,6 +4942,13 @@ F: Documentation/filesystems/caching/
F: fs/fscache/ F: fs/fscache/
F: include/linux/fscache*.h F: include/linux/fscache*.h
FS-CRYPTO: FILE SYSTEM LEVEL ENCRYPTION SUPPORT
M: Theodore Y. Ts'o <tytso@mit.edu>
M: Jaegeuk Kim <jaegeuk@kernel.org>
S: Supported
F: fs/crypto/
F: include/linux/fscrypto.h
F2FS FILE SYSTEM F2FS FILE SYSTEM
M: Jaegeuk Kim <jaegeuk@kernel.org> M: Jaegeuk Kim <jaegeuk@kernel.org>
M: Changman Lee <cm224.lee@samsung.com> M: Changman Lee <cm224.lee@samsung.com>
......
...@@ -1193,6 +1193,27 @@ static int ext2_has_free_blocks(struct ext2_sb_info *sbi) ...@@ -1193,6 +1193,27 @@ static int ext2_has_free_blocks(struct ext2_sb_info *sbi)
return 1; return 1;
} }
/*
* Returns 1 if the passed-in block region is valid; 0 if some part overlaps
* with filesystem metadata blocksi.
*/
int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk,
unsigned int count)
{
if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
(start_blk + count < start_blk) ||
(start_blk > le32_to_cpu(sbi->s_es->s_blocks_count)))
return 0;
/* Ensure we do not step over superblock */
if ((start_blk <= sbi->s_sb_block) &&
(start_blk + count >= sbi->s_sb_block))
return 0;
return 1;
}
/* /*
* ext2_new_blocks() -- core block(s) allocation function * ext2_new_blocks() -- core block(s) allocation function
* @inode: file inode * @inode: file inode
......
...@@ -367,6 +367,7 @@ struct ext2_inode { ...@@ -367,6 +367,7 @@ struct ext2_inode {
*/ */
#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ #define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
#define EXT2_ERROR_FS 0x0002 /* Errors detected */ #define EXT2_ERROR_FS 0x0002 /* Errors detected */
#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
/* /*
* Mount flags * Mount flags
...@@ -739,6 +740,8 @@ extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group); ...@@ -739,6 +740,8 @@ extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
extern ext2_fsblk_t ext2_new_block(struct inode *, unsigned long, int *); extern ext2_fsblk_t ext2_new_block(struct inode *, unsigned long, int *);
extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long, extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long,
unsigned long *, int *); unsigned long *, int *);
extern int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk,
unsigned int count);
extern void ext2_free_blocks (struct inode *, unsigned long, extern void ext2_free_blocks (struct inode *, unsigned long,
unsigned long); unsigned long);
extern unsigned long ext2_count_free_blocks (struct super_block *); extern unsigned long ext2_count_free_blocks (struct super_block *);
......
...@@ -1389,6 +1389,16 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) ...@@ -1389,6 +1389,16 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
ei->i_frag_size = raw_inode->i_fsize; ei->i_frag_size = raw_inode->i_fsize;
ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl); ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
ei->i_dir_acl = 0; ei->i_dir_acl = 0;
if (ei->i_file_acl &&
!ext2_data_block_valid(EXT2_SB(sb), ei->i_file_acl, 1)) {
ext2_error(sb, "ext2_iget", "bad extended attribute block %u",
ei->i_file_acl);
brelse(bh);
ret = -EFSCORRUPTED;
goto bad_inode;
}
if (S_ISREG(inode->i_mode)) if (S_ISREG(inode->i_mode))
inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32; inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
else else
......
...@@ -759,10 +759,19 @@ void ...@@ -759,10 +759,19 @@ void
ext2_xattr_delete_inode(struct inode *inode) ext2_xattr_delete_inode(struct inode *inode)
{ {
struct buffer_head *bh = NULL; struct buffer_head *bh = NULL;
struct ext2_sb_info *sbi = EXT2_SB(inode->i_sb);
down_write(&EXT2_I(inode)->xattr_sem); down_write(&EXT2_I(inode)->xattr_sem);
if (!EXT2_I(inode)->i_file_acl) if (!EXT2_I(inode)->i_file_acl)
goto cleanup; goto cleanup;
if (!ext2_data_block_valid(sbi, EXT2_I(inode)->i_file_acl, 0)) {
ext2_error(inode->i_sb, "ext2_xattr_delete_inode",
"inode %ld: xattr block %d is out of data blocks range",
inode->i_ino, EXT2_I(inode)->i_file_acl);
goto cleanup;
}
bh = sb_bread(inode->i_sb, EXT2_I(inode)->i_file_acl); bh = sb_bread(inode->i_sb, EXT2_I(inode)->i_file_acl);
if (!bh) { if (!bh) {
ext2_error(inode->i_sb, "ext2_xattr_delete_inode", ext2_error(inode->i_sb, "ext2_xattr_delete_inode",
......
...@@ -99,17 +99,9 @@ config EXT4_FS_SECURITY ...@@ -99,17 +99,9 @@ config EXT4_FS_SECURITY
extended attributes for file security labels, say N. extended attributes for file security labels, say N.
config EXT4_ENCRYPTION config EXT4_ENCRYPTION
tristate "Ext4 Encryption" bool "Ext4 Encryption"
depends on EXT4_FS depends on EXT4_FS
select CRYPTO_AES select FS_ENCRYPTION
select CRYPTO_CBC
select CRYPTO_ECB
select CRYPTO_XTS
select CRYPTO_CTS
select CRYPTO_CTR
select CRYPTO_SHA256
select KEYS
select ENCRYPTED_KEYS
help help
Enable encryption of ext4 files and directories. This Enable encryption of ext4 files and directories. This
feature is similar to ecryptfs, but it is more memory feature is similar to ecryptfs, but it is more memory
......
...@@ -12,5 +12,3 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \ ...@@ -12,5 +12,3 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.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
...@@ -208,6 +208,9 @@ static int ext4_init_block_bitmap(struct super_block *sb, ...@@ -208,6 +208,9 @@ static int ext4_init_block_bitmap(struct super_block *sb,
memset(bh->b_data, 0, sb->s_blocksize); memset(bh->b_data, 0, sb->s_blocksize);
bit_max = ext4_num_base_meta_clusters(sb, block_group); bit_max = ext4_num_base_meta_clusters(sb, block_group);
if ((bit_max >> 3) >= bh->b_size)
return -EFSCORRUPTED;
for (bit = 0; bit < bit_max; bit++) for (bit = 0; bit < bit_max; bit++)
ext4_set_bit(bit, bh->b_data); ext4_set_bit(bit, bh->b_data);
...@@ -610,7 +613,9 @@ int ext4_should_retry_alloc(struct super_block *sb, int *retries) ...@@ -610,7 +613,9 @@ int ext4_should_retry_alloc(struct super_block *sb, int *retries)
jbd_debug(1, "%s: retrying operation after ENOSPC\n", sb->s_id); jbd_debug(1, "%s: retrying operation after ENOSPC\n", sb->s_id);
jbd2_journal_force_commit_nested(EXT4_SB(sb)->s_journal); smp_mb();
if (EXT4_SB(sb)->s_mb_free_pending)
jbd2_journal_force_commit_nested(EXT4_SB(sb)->s_journal);
return 1; return 1;
} }
......
This diff is collapsed.
This diff is collapsed.
/*
* 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 <crypto/skcipher.h>
#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 derivation.
* @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 skcipher_request *req = NULL;
DECLARE_EXT4_COMPLETION_RESULT(ecr);
struct scatterlist src_sg, dst_sg;
struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
if (IS_ERR(tfm)) {
res = PTR_ERR(tfm);
tfm = NULL;
goto out;
}
crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
req = skcipher_request_alloc(tfm, GFP_NOFS);
if (!req) {
res = -ENOMEM;
goto out;
}
skcipher_request_set_callback(req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
derive_crypt_complete, &ecr);
res = crypto_skcipher_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);
skcipher_request_set_crypt(req, &src_sg, &dst_sg,
EXT4_AES_256_XTS_KEY_SIZE, NULL);
res = crypto_skcipher_encrypt(req);
if (res == -EINPROGRESS || res == -EBUSY) {
wait_for_completion(&ecr.completion);
res = ecr.res;
}
out:
skcipher_request_free(req);
crypto_free_skcipher(tfm);
return res;
}
void ext4_free_crypt_info(struct ext4_crypt_info *ci)
{
if (!ci)
return;
if (ci->ci_keyring_key)
key_put(ci->ci_keyring_key);
crypto_free_skcipher(ci->ci_ctfm);
kmem_cache_free(ext4_crypt_info_cachep, ci);
}
void ext4_free_encryption_info(struct inode *inode,
struct ext4_crypt_info *ci)
{
struct ext4_inode_info *ei = EXT4_I(inode);
struct ext4_crypt_info *prev;
if (ci == NULL)
ci = ACCESS_ONCE(ei->i_crypt_info);
if (ci == NULL)
return;
prev = cmpxchg(&ei->i_crypt_info, ci, NULL);
if (prev != ci)
return;
ext4_free_crypt_info(ci);
}
int _ext4_get_encryption_info(struct inode *inode)
{
struct ext4_inode_info *ei = EXT4_I(inode);
struct ext4_crypt_info *crypt_info;
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;
const struct user_key_payload *ukp;
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
struct crypto_skcipher *ctfm;
const char *cipher_str;
char raw_key[EXT4_MAX_KEY_SIZE];
char mode;
int res;
if (!ext4_read_workqueue) {
res = ext4_init_crypto();
if (res)
return res;
}
retry:
crypt_info = ACCESS_ONCE(ei->i_crypt_info);
if (crypt_info) {
if (!crypt_info->ci_keyring_key ||
key_validate(crypt_info->ci_keyring_key) == 0)
return 0;
ext4_free_encryption_info(inode, crypt_info);
goto retry;
}
res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
&ctx, sizeof(ctx));
if (res < 0) {
if (!DUMMY_ENCRYPTION_ENABLED(sbi))
return res;
ctx.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
ctx.filenames_encryption_mode =
EXT4_ENCRYPTION_MODE_AES_256_CTS;
ctx.flags = 0;
} else if (res != sizeof(ctx))
return -EINVAL;
res = 0;
crypt_info = kmem_cache_alloc(ext4_crypt_info_cachep, GFP_KERNEL);
if (!crypt_info)
return -ENOMEM;
crypt_info->ci_flags = ctx.flags;
crypt_info->ci_data_mode = ctx.contents_encryption_mode;
crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
crypt_info->ci_ctfm = NULL;
crypt_info->ci_keyring_key = NULL;
memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
sizeof(crypt_info->ci_master_key));
if (S_ISREG(inode->i_mode))
mode = crypt_info->ci_data_mode;
else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
mode = crypt_info->ci_filename_mode;
else
BUG();
switch (mode) {
case EXT4_ENCRYPTION_MODE_AES_256_XTS:
cipher_str = "xts(aes)";
break;
case EXT4_ENCRYPTION_MODE_AES_256_CTS:
cipher_str = "cts(cbc(aes))";
break;
default:
printk_once(KERN_WARNING
"ext4: unsupported key mode %d (ino %u)\n",
mode, (unsigned) inode->i_ino);
res = -ENOKEY;
goto out;
}
if (DUMMY_ENCRYPTION_ENABLED(sbi)) {
memset(raw_key, 0x42, EXT4_AES_256_XTS_KEY_SIZE);
goto got_key;
}
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;
}
crypt_info->ci_keyring_key = keyring_key;
if (keyring_key->type != &key_type_logon) {
printk_once(KERN_WARNING
"ext4: key type must be logon\n");
res = -ENOKEY;
goto out;
}
down_read(&keyring_key->sem);
ukp = user_key_payload(keyring_key);
if (ukp->datalen != sizeof(struct ext4_encryption_key)) {
res = -EINVAL;
up_read(&keyring_key->sem);
goto out;
}
master_key = (struct ext4_encryption_key *)ukp->data;
BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE !=
EXT4_KEY_DERIVATION_NONCE_SIZE);
if (master_key->size != EXT4_AES_256_XTS_KEY_SIZE) {
printk_once(KERN_WARNING
"ext4: key size incorrect: %d\n",
master_key->size);
res = -ENOKEY;
up_read(&keyring_key->sem);
goto out;
}
res = ext4_derive_key_aes(ctx.nonce, master_key->raw,
raw_key);
up_read(&keyring_key->sem);
if (res)
goto out;
got_key:
ctfm = crypto_alloc_skcipher(cipher_str, 0, 0);
if (!ctfm || IS_ERR(ctfm)) {
res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
printk(KERN_DEBUG
"%s: error %d (inode %u) allocating crypto tfm\n",
__func__, res, (unsigned) inode->i_ino);
goto out;
}
crypt_info->ci_ctfm = ctfm;
crypto_skcipher_clear_flags(ctfm, ~0);
crypto_tfm_set_flags(crypto_skcipher_tfm(ctfm),
CRYPTO_TFM_REQ_WEAK_KEY);
res = crypto_skcipher_setkey(ctfm, raw_key,
ext4_encryption_key_size(mode));
if (res)
goto out;
memzero_explicit(raw_key, sizeof(raw_key));
if (cmpxchg(&ei->i_crypt_info, NULL, crypt_info) != NULL) {
ext4_free_crypt_info(crypt_info);
goto retry;
}
return 0;
out:
if (res == -ENOKEY)
res = 0;
ext4_free_crypt_info(crypt_info);
memzero_explicit(raw_key, sizeof(raw_key));
return res;
}
int ext4_has_encryption_key(struct inode *inode)
{
struct ext4_inode_info *ei = EXT4_I(inode);
return (ei->i_crypt_info != NULL);
}
/*
* 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_jbd2.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.flags ==
policy->flags) &&
(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;
handle_t *handle;
int res, res2;
res = ext4_convert_inline_data(inode);
if (res)
return res;
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);
return -EINVAL;
}
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);
return -EINVAL;
}
if (policy->flags & ~EXT4_POLICY_FLAGS_VALID)
return -EINVAL;
ctx.contents_encryption_mode = policy->contents_encryption_mode;
ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
ctx.flags = policy->flags;
BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
handle = ext4_journal_start(inode, EXT4_HT_MISC,
ext4_jbd2_credits_xattr(inode));
if (IS_ERR(handle))
return PTR_ERR(handle);
res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
sizeof(ctx), 0);
if (!res) {
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
res = ext4_mark_inode_dirty(handle, inode);
if (res)
EXT4_ERROR_INODE(inode, "Failed to mark inode dirty");
}
res2 = ext4_journal_stop(handle);
if (!res)
res = res2;
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 (!S_ISDIR(inode->i_mode))
return -EINVAL;
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;
policy->flags = ctx.flags;
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_crypt_info *parent_ci, *child_ci;
int res;
if ((parent == NULL) || (child == NULL)) {
pr_err("parent %p child %p\n", parent, child);
WARN_ON(1); /* Should never happen */
return 0;
}
/* no restrictions if the parent directory is not encrypted */
if (!ext4_encrypted_inode(parent))
return 1;
/* if the child directory is not encrypted, this is always a problem */
if (!ext4_encrypted_inode(child))
return 0;
res = ext4_get_encryption_info(parent);
if (res)
return 0;
res = ext4_get_encryption_info(child);
if (res)
return 0;
parent_ci = EXT4_I(parent)->i_crypt_info;
child_ci = EXT4_I(child)->i_crypt_info;
if (!parent_ci && !child_ci)
return 1;
if (!parent_ci || !child_ci)
return 0;
return (memcmp(parent_ci->ci_master_key,
child_ci->ci_master_key,
EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
(parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
(parent_ci->ci_filename_mode == child_ci->ci_filename_mode) &&
(parent_ci->ci_flags == child_ci->ci_flags));
}
/**
* 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;
struct ext4_crypt_info *ci;
int res;
res = ext4_get_encryption_info(parent);
if (res < 0)
return res;
ci = EXT4_I(parent)->i_crypt_info;
if (ci == NULL)
return -ENOKEY;
ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V1;
if (DUMMY_ENCRYPTION_ENABLED(EXT4_SB(parent->i_sb))) {
ctx.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
ctx.filenames_encryption_mode =
EXT4_ENCRYPTION_MODE_AES_256_CTS;
ctx.flags = 0;
memset(ctx.master_key_descriptor, 0x42,
EXT4_KEY_DESCRIPTOR_SIZE);
res = 0;
} else {
ctx.contents_encryption_mode = ci->ci_data_mode;
ctx.filenames_encryption_mode = ci->ci_filename_mode;
ctx.flags = ci->ci_flags;
memcpy(ctx.master_key_descriptor, ci->ci_master_key,
EXT4_KEY_DESCRIPTOR_SIZE);
}
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);
if (!res) {
ext4_set_inode_flag(child, EXT4_INODE_ENCRYPT);
ext4_clear_inode_state(child, EXT4_STATE_MAY_INLINE_DATA);
res = ext4_get_encryption_info(child);
}
return res;
}
...@@ -109,10 +109,10 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) ...@@ -109,10 +109,10 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
struct buffer_head *bh = NULL; struct buffer_head *bh = NULL;
int dir_has_error = 0; int dir_has_error = 0;
struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; struct fscrypt_str fstr = FSTR_INIT(NULL, 0);
if (ext4_encrypted_inode(inode)) { if (ext4_encrypted_inode(inode)) {
err = ext4_get_encryption_info(inode); err = fscrypt_get_encryption_info(inode);
if (err && err != -ENOKEY) if (err && err != -ENOKEY)
return err; return err;
} }
...@@ -139,8 +139,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) ...@@ -139,8 +139,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
} }
if (ext4_encrypted_inode(inode)) { if (ext4_encrypted_inode(inode)) {
err = ext4_fname_crypto_alloc_buffer(inode, EXT4_NAME_LEN, err = fscrypt_fname_alloc_buffer(inode, EXT4_NAME_LEN, &fstr);
&fname_crypto_str);
if (err < 0) if (err < 0)
return err; return err;
} }
...@@ -253,16 +252,19 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) ...@@ -253,16 +252,19 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
get_dtype(sb, de->file_type))) get_dtype(sb, de->file_type)))
goto done; goto done;
} else { } else {
int save_len = fname_crypto_str.len; int save_len = fstr.len;
struct fscrypt_str de_name =
FSTR_INIT(de->name,
de->name_len);
/* Directory is encrypted */ /* Directory is encrypted */
err = ext4_fname_disk_to_usr(inode, err = fscrypt_fname_disk_to_usr(inode,
NULL, de, &fname_crypto_str); 0, 0, &de_name, &fstr);
fname_crypto_str.len = save_len; fstr.len = save_len;
if (err < 0) if (err < 0)
goto errout; goto errout;
if (!dir_emit(ctx, if (!dir_emit(ctx,
fname_crypto_str.name, err, fstr.name, err,
le32_to_cpu(de->inode), le32_to_cpu(de->inode),
get_dtype(sb, de->file_type))) get_dtype(sb, de->file_type)))
goto done; goto done;
...@@ -281,7 +283,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) ...@@ -281,7 +283,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
err = 0; err = 0;
errout: errout:
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
ext4_fname_crypto_free_buffer(&fname_crypto_str); fscrypt_fname_free_buffer(&fstr);
#endif #endif
brelse(bh); brelse(bh);
return err; return err;
...@@ -432,7 +434,7 @@ void ext4_htree_free_dir_info(struct dir_private_info *p) ...@@ -432,7 +434,7 @@ void ext4_htree_free_dir_info(struct dir_private_info *p)
int ext4_htree_store_dirent(struct file *dir_file, __u32 hash, int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
__u32 minor_hash, __u32 minor_hash,
struct ext4_dir_entry_2 *dirent, struct ext4_dir_entry_2 *dirent,
struct ext4_str *ent_name) struct fscrypt_str *ent_name)
{ {
struct rb_node **p, *parent = NULL; struct rb_node **p, *parent = NULL;
struct fname *fname, *new_fn; struct fname *fname, *new_fn;
...@@ -609,7 +611,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx) ...@@ -609,7 +611,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
static int ext4_dir_open(struct inode * inode, struct file * filp) static int ext4_dir_open(struct inode * inode, struct file * filp)
{ {
if (ext4_encrypted_inode(inode)) if (ext4_encrypted_inode(inode))
return ext4_get_encryption_info(inode) ? -EACCES : 0; return fscrypt_get_encryption_info(inode) ? -EACCES : 0;
return 0; return 0;
} }
......
This diff is collapsed.
/*
* 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 flags;
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
} __attribute__((__packed__));
#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V1 1
#define EXT4_KEY_DERIVATION_NONCE_SIZE 16
#define EXT4_POLICY_FLAGS_PAD_4 0x00
#define EXT4_POLICY_FLAGS_PAD_8 0x01
#define EXT4_POLICY_FLAGS_PAD_16 0x02
#define EXT4_POLICY_FLAGS_PAD_32 0x03
#define EXT4_POLICY_FLAGS_PAD_MASK 0x03
#define EXT4_POLICY_FLAGS_VALID 0x03
/**
* 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 flags;
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
/* This is passed in from userspace into the kernel keyring */
struct ext4_encryption_key {
__u32 mode;
char raw[EXT4_MAX_KEY_SIZE];
__u32 size;
} __attribute__((__packed__));
struct ext4_crypt_info {
char ci_data_mode;
char ci_filename_mode;
char ci_flags;
struct crypto_skcipher *ci_ctfm;
struct key *ci_keyring_key;
char ci_master_key[EXT4_KEY_DESCRIPTOR_SIZE];
};
#define EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
#define EXT4_WRITE_PATH_FL 0x00000002
struct ext4_crypto_ctx {
union {
struct {
struct page *bounce_page; /* Ciphertext page */
struct page *control_page; /* Original page */
} w;
struct {
struct bio *bio;
struct work_struct work;
} r;
struct list_head free_list; /* Free list */
};
char flags; /* Flags */
char 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;
};
/**
* 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 */
...@@ -175,6 +175,13 @@ struct ext4_journal_cb_entry { ...@@ -175,6 +175,13 @@ struct ext4_journal_cb_entry {
* There is no guaranteed calling order of multiple registered callbacks on * There is no guaranteed calling order of multiple registered callbacks on
* the same transaction. * the same transaction.
*/ */
static inline void _ext4_journal_callback_add(handle_t *handle,
struct ext4_journal_cb_entry *jce)
{
/* Add the jce to transaction's private list */
list_add_tail(&jce->jce_list, &handle->h_transaction->t_private_list);
}
static inline void ext4_journal_callback_add(handle_t *handle, static inline void ext4_journal_callback_add(handle_t *handle,
void (*func)(struct super_block *sb, void (*func)(struct super_block *sb,
struct ext4_journal_cb_entry *jce, struct ext4_journal_cb_entry *jce,
...@@ -187,10 +194,11 @@ static inline void ext4_journal_callback_add(handle_t *handle, ...@@ -187,10 +194,11 @@ static inline void ext4_journal_callback_add(handle_t *handle,
/* Add the jce to transaction's private list */ /* Add the jce to transaction's private list */
jce->jce_func = func; jce->jce_func = func;
spin_lock(&sbi->s_md_lock); spin_lock(&sbi->s_md_lock);
list_add_tail(&jce->jce_list, &handle->h_transaction->t_private_list); _ext4_journal_callback_add(handle, jce);
spin_unlock(&sbi->s_md_lock); spin_unlock(&sbi->s_md_lock);
} }
/** /**
* ext4_journal_callback_del: delete a registered callback * ext4_journal_callback_del: delete a registered callback
* @handle: active journal transaction handle on which callback was registered * @handle: active journal transaction handle on which callback was registered
......
...@@ -381,9 +381,13 @@ static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext) ...@@ -381,9 +381,13 @@ static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext)
ext4_fsblk_t block = ext4_ext_pblock(ext); ext4_fsblk_t block = ext4_ext_pblock(ext);
int len = ext4_ext_get_actual_len(ext); int len = ext4_ext_get_actual_len(ext);
ext4_lblk_t lblock = le32_to_cpu(ext->ee_block); ext4_lblk_t lblock = le32_to_cpu(ext->ee_block);
ext4_lblk_t last = lblock + len - 1;
if (len == 0 || lblock > last) /*
* We allow neither:
* - zero length
* - overflow/wrap-around
*/
if (lblock + len <= lblock)
return 0; return 0;
return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len); return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len);
} }
...@@ -474,6 +478,10 @@ static int __ext4_ext_check(const char *function, unsigned int line, ...@@ -474,6 +478,10 @@ static int __ext4_ext_check(const char *function, unsigned int line,
error_msg = "invalid extent entries"; error_msg = "invalid extent entries";
goto corrupted; goto corrupted;
} }
if (unlikely(depth > 32)) {
error_msg = "too large eh_depth";
goto corrupted;
}
/* Verify checksum on non-root extent tree nodes */ /* Verify checksum on non-root extent tree nodes */
if (ext_depth(inode) != depth && if (ext_depth(inode) != depth &&
!ext4_extent_block_csum_verify(inode, eh)) { !ext4_extent_block_csum_verify(inode, eh)) {
......
...@@ -303,10 +303,10 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -303,10 +303,10 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
struct inode *inode = file->f_mapping->host; struct inode *inode = file->f_mapping->host;
if (ext4_encrypted_inode(inode)) { if (ext4_encrypted_inode(inode)) {
int err = ext4_get_encryption_info(inode); int err = fscrypt_get_encryption_info(inode);
if (err) if (err)
return 0; return 0;
if (ext4_encryption_info(inode) == NULL) if (!fscrypt_has_encryption_key(inode))
return -ENOKEY; return -ENOKEY;
} }
file_accessed(file); file_accessed(file);
...@@ -362,16 +362,16 @@ static int ext4_file_open(struct inode * inode, struct file * filp) ...@@ -362,16 +362,16 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
} }
} }
if (ext4_encrypted_inode(inode)) { if (ext4_encrypted_inode(inode)) {
ret = ext4_get_encryption_info(inode); ret = fscrypt_get_encryption_info(inode);
if (ret) if (ret)
return -EACCES; return -EACCES;
if (ext4_encryption_info(inode) == NULL) if (!fscrypt_has_encryption_key(inode))
return -ENOKEY; return -ENOKEY;
} }
dir = dget_parent(file_dentry(filp)); dir = dget_parent(file_dentry(filp));
if (ext4_encrypted_inode(d_inode(dir)) && if (ext4_encrypted_inode(d_inode(dir)) &&
!ext4_is_child_context_consistent_with_parent(d_inode(dir), inode)) { !fscrypt_has_permitted_context(d_inode(dir), inode)) {
ext4_warning(inode->i_sb, ext4_warning(inode->i_sb,
"Inconsistent encryption contexts: %lu/%lu", "Inconsistent encryption contexts: %lu/%lu",
(unsigned long) d_inode(dir)->i_ino, (unsigned long) d_inode(dir)->i_ino,
......
...@@ -106,9 +106,11 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -106,9 +106,11 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
} }
if (!journal) { if (!journal) {
ret = generic_file_fsync(file, start, end, datasync); ret = __generic_file_fsync(file, start, end, datasync);
if (!ret && !hlist_empty(&inode->i_dentry)) if (!ret && !hlist_empty(&inode->i_dentry))
ret = ext4_sync_parent(inode); ret = ext4_sync_parent(inode);
if (test_opt(inode->i_sb, BARRIER))
goto issue_flush;
goto out; goto out;
} }
...@@ -140,6 +142,7 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -140,6 +142,7 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
needs_barrier = true; needs_barrier = true;
ret = jbd2_complete_transaction(journal, commit_tid); ret = jbd2_complete_transaction(journal, commit_tid);
if (needs_barrier) { if (needs_barrier) {
issue_flush:
err = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); err = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
if (!ret) if (!ret)
ret = err; ret = err;
......
...@@ -767,10 +767,10 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir, ...@@ -767,10 +767,10 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
if ((ext4_encrypted_inode(dir) || if ((ext4_encrypted_inode(dir) ||
DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) && DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) { (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) {
err = ext4_get_encryption_info(dir); err = fscrypt_get_encryption_info(dir);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
if (ext4_encryption_info(dir) == NULL) if (!fscrypt_has_encryption_key(dir))
return ERR_PTR(-EPERM); return ERR_PTR(-EPERM);
if (!handle) if (!handle)
nblocks += EXT4_DATA_TRANS_BLOCKS(dir->i_sb); nblocks += EXT4_DATA_TRANS_BLOCKS(dir->i_sb);
...@@ -1115,7 +1115,8 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir, ...@@ -1115,7 +1115,8 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
} }
if (encrypt) { if (encrypt) {
err = ext4_inherit_context(dir, inode); /* give pointer to avoid set_context with journal ops. */
err = fscrypt_inherit_context(dir, inode, &encrypt, true);
if (err) if (err)
goto fail_free_drop; goto fail_free_drop;
} }
......
...@@ -1326,7 +1326,7 @@ int htree_inlinedir_to_tree(struct file *dir_file, ...@@ -1326,7 +1326,7 @@ int htree_inlinedir_to_tree(struct file *dir_file,
struct ext4_iloc iloc; struct ext4_iloc iloc;
void *dir_buf = NULL; void *dir_buf = NULL;
struct ext4_dir_entry_2 fake; struct ext4_dir_entry_2 fake;
struct ext4_str tmp_str; struct fscrypt_str tmp_str;
ret = ext4_get_inode_loc(inode, &iloc); ret = ext4_get_inode_loc(inode, &iloc);
if (ret) if (ret)
...@@ -1739,20 +1739,20 @@ ext4_get_inline_entry(struct inode *inode, ...@@ -1739,20 +1739,20 @@ ext4_get_inline_entry(struct inode *inode,
return (struct ext4_dir_entry_2 *)(inline_pos + offset); return (struct ext4_dir_entry_2 *)(inline_pos + offset);
} }
int empty_inline_dir(struct inode *dir, int *has_inline_data) bool empty_inline_dir(struct inode *dir, int *has_inline_data)
{ {
int err, inline_size; int err, inline_size;
struct ext4_iloc iloc; struct ext4_iloc iloc;
void *inline_pos; void *inline_pos;
unsigned int offset; unsigned int offset;
struct ext4_dir_entry_2 *de; struct ext4_dir_entry_2 *de;
int ret = 1; bool ret = true;
err = ext4_get_inode_loc(dir, &iloc); err = ext4_get_inode_loc(dir, &iloc);
if (err) { if (err) {
EXT4_ERROR_INODE(dir, "error %d getting inode %lu block", EXT4_ERROR_INODE(dir, "error %d getting inode %lu block",
err, dir->i_ino); err, dir->i_ino);
return 1; return true;
} }
down_read(&EXT4_I(dir)->xattr_sem); down_read(&EXT4_I(dir)->xattr_sem);
...@@ -1766,7 +1766,7 @@ int empty_inline_dir(struct inode *dir, int *has_inline_data) ...@@ -1766,7 +1766,7 @@ int empty_inline_dir(struct inode *dir, int *has_inline_data)
ext4_warning(dir->i_sb, ext4_warning(dir->i_sb,
"bad inline directory (dir #%lu) - no `..'", "bad inline directory (dir #%lu) - no `..'",
dir->i_ino); dir->i_ino);
ret = 1; ret = true;
goto out; goto out;
} }
...@@ -1784,11 +1784,11 @@ int empty_inline_dir(struct inode *dir, int *has_inline_data) ...@@ -1784,11 +1784,11 @@ int empty_inline_dir(struct inode *dir, int *has_inline_data)
dir->i_ino, le32_to_cpu(de->inode), dir->i_ino, le32_to_cpu(de->inode),
le16_to_cpu(de->rec_len), de->name_len, le16_to_cpu(de->rec_len), de->name_len,
inline_size); inline_size);
ret = 1; ret = true;
goto out; goto out;
} }
if (le32_to_cpu(de->inode)) { if (le32_to_cpu(de->inode)) {
ret = 0; ret = false;
goto out; goto out;
} }
offset += ext4_rec_len_from_disk(de->rec_len, inline_size); offset += ext4_rec_len_from_disk(de->rec_len, inline_size);
......
...@@ -51,25 +51,31 @@ static __u32 ext4_inode_csum(struct inode *inode, struct ext4_inode *raw, ...@@ -51,25 +51,31 @@ static __u32 ext4_inode_csum(struct inode *inode, struct ext4_inode *raw,
struct ext4_inode_info *ei) struct ext4_inode_info *ei)
{ {
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
__u16 csum_lo;
__u16 csum_hi = 0;
__u32 csum; __u32 csum;
__u16 dummy_csum = 0;
int offset = offsetof(struct ext4_inode, i_checksum_lo);
unsigned int csum_size = sizeof(dummy_csum);
csum_lo = le16_to_cpu(raw->i_checksum_lo); csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)raw, offset);
raw->i_checksum_lo = 0; csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, csum_size);
if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE && offset += csum_size;
EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) { csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset,
csum_hi = le16_to_cpu(raw->i_checksum_hi); EXT4_GOOD_OLD_INODE_SIZE - offset);
raw->i_checksum_hi = 0;
}
csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)raw,
EXT4_INODE_SIZE(inode->i_sb));
raw->i_checksum_lo = cpu_to_le16(csum_lo); if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE && offset = offsetof(struct ext4_inode, i_checksum_hi);
EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) csum = ext4_chksum(sbi, csum, (__u8 *)raw +
raw->i_checksum_hi = cpu_to_le16(csum_hi); EXT4_GOOD_OLD_INODE_SIZE,
offset - EXT4_GOOD_OLD_INODE_SIZE);
if (EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) {
csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum,
csum_size);
offset += csum_size;
csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset,
EXT4_INODE_SIZE(inode->i_sb) -
offset);
}
}
return csum; return csum;
} }
...@@ -205,9 +211,9 @@ void ext4_evict_inode(struct inode *inode) ...@@ -205,9 +211,9 @@ void ext4_evict_inode(struct inode *inode)
* Note that directories do not have this problem because they * Note that directories do not have this problem because they
* don't use page cache. * don't use page cache.
*/ */
if (ext4_should_journal_data(inode) && if (inode->i_ino != EXT4_JOURNAL_INO &&
(S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode)) && ext4_should_journal_data(inode) &&
inode->i_ino != EXT4_JOURNAL_INO) { (S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode))) {
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal; journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
tid_t commit_tid = EXT4_I(inode)->i_datasync_tid; tid_t commit_tid = EXT4_I(inode)->i_datasync_tid;
...@@ -386,7 +392,7 @@ int ext4_issue_zeroout(struct inode *inode, ext4_lblk_t lblk, ext4_fsblk_t pblk, ...@@ -386,7 +392,7 @@ int ext4_issue_zeroout(struct inode *inode, ext4_lblk_t lblk, ext4_fsblk_t pblk,
int ret; int ret;
if (ext4_encrypted_inode(inode)) if (ext4_encrypted_inode(inode))
return ext4_encrypted_zeroout(inode, lblk, pblk, len); return fscrypt_zeroout_range(inode, lblk, pblk, len);
ret = sb_issue_zeroout(inode->i_sb, pblk, len, GFP_NOFS); ret = sb_issue_zeroout(inode->i_sb, pblk, len, GFP_NOFS);
if (ret > 0) if (ret > 0)
...@@ -1152,7 +1158,7 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, ...@@ -1152,7 +1158,7 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
if (unlikely(err)) if (unlikely(err))
page_zero_new_buffers(page, from, to); page_zero_new_buffers(page, from, to);
else if (decrypt) else if (decrypt)
err = ext4_decrypt(page); err = fscrypt_decrypt_page(page);
return err; return err;
} }
#endif #endif
...@@ -2748,13 +2754,36 @@ static int ext4_writepages(struct address_space *mapping, ...@@ -2748,13 +2754,36 @@ static int ext4_writepages(struct address_space *mapping,
done = true; done = true;
} }
} }
ext4_journal_stop(handle); /*
* Caution: If the handle is synchronous,
* ext4_journal_stop() can wait for transaction commit
* to finish which may depend on writeback of pages to
* complete or on page lock to be released. In that
* case, we have to wait until after after we have
* submitted all the IO, released page locks we hold,
* and dropped io_end reference (for extent conversion
* to be able to complete) before stopping the handle.
*/
if (!ext4_handle_valid(handle) || handle->h_sync == 0) {
ext4_journal_stop(handle);
handle = NULL;
}
/* Submit prepared bio */ /* Submit prepared bio */
ext4_io_submit(&mpd.io_submit); ext4_io_submit(&mpd.io_submit);
/* Unlock pages we didn't use */ /* Unlock pages we didn't use */
mpage_release_unused_pages(&mpd, give_up_on_write); mpage_release_unused_pages(&mpd, give_up_on_write);
/* Drop our io_end reference we got from init */ /*
ext4_put_io_end(mpd.io_submit.io_end); * Drop our io_end reference we got from init. We have
* to be careful and use deferred io_end finishing if
* we are still holding the transaction as we can
* release the last reference to io_end which may end
* up doing unwritten extent conversion.
*/
if (handle) {
ext4_put_io_end_defer(mpd.io_submit.io_end);
ext4_journal_stop(handle);
} else
ext4_put_io_end(mpd.io_submit.io_end);
if (ret == -ENOSPC && sbi->s_journal) { if (ret == -ENOSPC && sbi->s_journal) {
/* /*
...@@ -3706,9 +3735,9 @@ static int __ext4_block_zero_page_range(handle_t *handle, ...@@ -3706,9 +3735,9 @@ static int __ext4_block_zero_page_range(handle_t *handle,
if (S_ISREG(inode->i_mode) && if (S_ISREG(inode->i_mode) &&
ext4_encrypted_inode(inode)) { ext4_encrypted_inode(inode)) {
/* We expect the key to be set. */ /* We expect the key to be set. */
BUG_ON(!ext4_has_encryption_key(inode)); BUG_ON(!fscrypt_has_encryption_key(inode));
BUG_ON(blocksize != PAGE_SIZE); BUG_ON(blocksize != PAGE_SIZE);
WARN_ON_ONCE(ext4_decrypt(page)); WARN_ON_ONCE(fscrypt_decrypt_page(page));
} }
} }
if (ext4_should_journal_data(inode)) { if (ext4_should_journal_data(inode)) {
......
...@@ -308,6 +308,7 @@ static int ext4_ioctl_setproject(struct file *filp, __u32 projid) ...@@ -308,6 +308,7 @@ static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
kprojid_t kprojid; kprojid_t kprojid;
struct ext4_iloc iloc; struct ext4_iloc iloc;
struct ext4_inode *raw_inode; struct ext4_inode *raw_inode;
struct dquot *transfer_to[MAXQUOTAS] = { };
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_PROJECT)) { EXT4_FEATURE_RO_COMPAT_PROJECT)) {
...@@ -361,17 +362,14 @@ static int ext4_ioctl_setproject(struct file *filp, __u32 projid) ...@@ -361,17 +362,14 @@ static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
if (err) if (err)
goto out_stop; goto out_stop;
if (sb_has_quota_limits_enabled(sb, PRJQUOTA)) { transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
struct dquot *transfer_to[MAXQUOTAS] = { }; if (!IS_ERR(transfer_to[PRJQUOTA])) {
err = __dquot_transfer(inode, transfer_to);
transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid)); dqput(transfer_to[PRJQUOTA]);
if (!IS_ERR(transfer_to[PRJQUOTA])) { if (err)
err = __dquot_transfer(inode, transfer_to); goto out_dirty;
dqput(transfer_to[PRJQUOTA]);
if (err)
goto out_dirty;
}
} }
EXT4_I(inode)->i_projid = kprojid; EXT4_I(inode)->i_projid = kprojid;
inode->i_ctime = ext4_current_time(inode); inode->i_ctime = ext4_current_time(inode);
out_dirty: out_dirty:
...@@ -772,19 +770,13 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -772,19 +770,13 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return ext4_ext_precache(inode); return ext4_ext_precache(inode);
case EXT4_IOC_SET_ENCRYPTION_POLICY: { case EXT4_IOC_SET_ENCRYPTION_POLICY: {
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
struct ext4_encryption_policy policy; struct fscrypt_policy policy;
int err = 0;
if (copy_from_user(&policy, if (copy_from_user(&policy,
(struct ext4_encryption_policy __user *)arg, (struct fscrypt_policy __user *)arg,
sizeof(policy))) { sizeof(policy)))
err = -EFAULT; return -EFAULT;
goto encryption_policy_out; return fscrypt_process_policy(inode, &policy);
}
err = ext4_process_policy(&policy, inode);
encryption_policy_out:
return err;
#else #else
return -EOPNOTSUPP; return -EOPNOTSUPP;
#endif #endif
...@@ -827,12 +819,12 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -827,12 +819,12 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
} }
case EXT4_IOC_GET_ENCRYPTION_POLICY: { case EXT4_IOC_GET_ENCRYPTION_POLICY: {
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
struct ext4_encryption_policy policy; struct fscrypt_policy policy;
int err = 0; int err = 0;
if (!ext4_encrypted_inode(inode)) if (!ext4_encrypted_inode(inode))
return -ENOENT; return -ENOENT;
err = ext4_get_policy(inode, &policy); err = fscrypt_get_policy(inode, &policy);
if (err) if (err)
return err; return err;
if (copy_to_user((void __user *)arg, &policy, sizeof(policy))) if (copy_to_user((void __user *)arg, &policy, sizeof(policy)))
......
...@@ -2627,6 +2627,7 @@ int ext4_mb_init(struct super_block *sb) ...@@ -2627,6 +2627,7 @@ int ext4_mb_init(struct super_block *sb)
spin_lock_init(&sbi->s_md_lock); spin_lock_init(&sbi->s_md_lock);
spin_lock_init(&sbi->s_bal_lock); spin_lock_init(&sbi->s_bal_lock);
sbi->s_mb_free_pending = 0;
sbi->s_mb_max_to_scan = MB_DEFAULT_MAX_TO_SCAN; sbi->s_mb_max_to_scan = MB_DEFAULT_MAX_TO_SCAN;
sbi->s_mb_min_to_scan = MB_DEFAULT_MIN_TO_SCAN; sbi->s_mb_min_to_scan = MB_DEFAULT_MIN_TO_SCAN;
...@@ -2814,6 +2815,9 @@ static void ext4_free_data_callback(struct super_block *sb, ...@@ -2814,6 +2815,9 @@ static void ext4_free_data_callback(struct super_block *sb,
/* we expect to find existing buddy because it's pinned */ /* we expect to find existing buddy because it's pinned */
BUG_ON(err != 0); BUG_ON(err != 0);
spin_lock(&EXT4_SB(sb)->s_md_lock);
EXT4_SB(sb)->s_mb_free_pending -= entry->efd_count;
spin_unlock(&EXT4_SB(sb)->s_md_lock);
db = e4b.bd_info; db = e4b.bd_info;
/* there are blocks to put in buddy to make them really free */ /* there are blocks to put in buddy to make them really free */
...@@ -2939,7 +2943,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, ...@@ -2939,7 +2943,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
ext4_error(sb, "Allocating blocks %llu-%llu which overlap " ext4_error(sb, "Allocating blocks %llu-%llu which overlap "
"fs metadata", block, block+len); "fs metadata", block, block+len);
/* File system mounted not to panic on error /* File system mounted not to panic on error
* Fix the bitmap and repeat the block allocation * Fix the bitmap and return EFSCORRUPTED
* We leak some of the blocks here. * We leak some of the blocks here.
*/ */
ext4_lock_group(sb, ac->ac_b_ex.fe_group); ext4_lock_group(sb, ac->ac_b_ex.fe_group);
...@@ -2948,7 +2952,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, ...@@ -2948,7 +2952,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
ext4_unlock_group(sb, ac->ac_b_ex.fe_group); ext4_unlock_group(sb, ac->ac_b_ex.fe_group);
err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh); err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh);
if (!err) if (!err)
err = -EAGAIN; err = -EFSCORRUPTED;
goto out_err; goto out_err;
} }
...@@ -4513,18 +4517,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, ...@@ -4513,18 +4517,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
} }
if (likely(ac->ac_status == AC_STATUS_FOUND)) { if (likely(ac->ac_status == AC_STATUS_FOUND)) {
*errp = ext4_mb_mark_diskspace_used(ac, handle, reserv_clstrs); *errp = ext4_mb_mark_diskspace_used(ac, handle, reserv_clstrs);
if (*errp == -EAGAIN) { if (*errp) {
/*
* drop the reference that we took
* in ext4_mb_use_best_found
*/
ext4_mb_release_context(ac);
ac->ac_b_ex.fe_group = 0;
ac->ac_b_ex.fe_start = 0;
ac->ac_b_ex.fe_len = 0;
ac->ac_status = AC_STATUS_CONTINUE;
goto repeat;
} else if (*errp) {
ext4_discard_allocated_blocks(ac); ext4_discard_allocated_blocks(ac);
goto errout; goto errout;
} else { } else {
...@@ -4583,6 +4576,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b, ...@@ -4583,6 +4576,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
{ {
ext4_group_t group = e4b->bd_group; ext4_group_t group = e4b->bd_group;
ext4_grpblk_t cluster; ext4_grpblk_t cluster;
ext4_grpblk_t clusters = new_entry->efd_count;
struct ext4_free_data *entry; struct ext4_free_data *entry;
struct ext4_group_info *db = e4b->bd_info; struct ext4_group_info *db = e4b->bd_info;
struct super_block *sb = e4b->bd_sb; struct super_block *sb = e4b->bd_sb;
...@@ -4649,8 +4643,11 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b, ...@@ -4649,8 +4643,11 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
} }
} }
/* Add the extent to transaction's private list */ /* Add the extent to transaction's private list */
ext4_journal_callback_add(handle, ext4_free_data_callback, new_entry->efd_jce.jce_func = ext4_free_data_callback;
&new_entry->efd_jce); spin_lock(&sbi->s_md_lock);
_ext4_journal_callback_add(handle, &new_entry->efd_jce);
sbi->s_mb_free_pending += clusters;
spin_unlock(&sbi->s_md_lock);
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/fscrypto.h>
#include "ext4_jbd2.h" #include "ext4_jbd2.h"
#include "xattr.h" #include "xattr.h"
...@@ -67,7 +68,6 @@ static void ext4_finish_bio(struct bio *bio) ...@@ -67,7 +68,6 @@ static void ext4_finish_bio(struct bio *bio)
struct page *page = bvec->bv_page; struct page *page = bvec->bv_page;
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
struct page *data_page = NULL; struct page *data_page = NULL;
struct ext4_crypto_ctx *ctx = NULL;
#endif #endif
struct buffer_head *bh, *head; struct buffer_head *bh, *head;
unsigned bio_start = bvec->bv_offset; unsigned bio_start = bvec->bv_offset;
...@@ -82,8 +82,7 @@ static void ext4_finish_bio(struct bio *bio) ...@@ -82,8 +82,7 @@ static void ext4_finish_bio(struct bio *bio)
if (!page->mapping) { if (!page->mapping) {
/* The bounce data pages are unmapped. */ /* The bounce data pages are unmapped. */
data_page = page; data_page = page;
ctx = (struct ext4_crypto_ctx *)page_private(data_page); fscrypt_pullback_bio_page(&page, false);
page = ctx->w.control_page;
} }
#endif #endif
...@@ -113,8 +112,8 @@ static void ext4_finish_bio(struct bio *bio) ...@@ -113,8 +112,8 @@ static void ext4_finish_bio(struct bio *bio)
local_irq_restore(flags); local_irq_restore(flags);
if (!under_io) { if (!under_io) {
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
if (ctx) if (data_page)
ext4_restore_control_page(data_page); fscrypt_restore_control_page(data_page);
#endif #endif
end_page_writeback(page); end_page_writeback(page);
} }
...@@ -473,7 +472,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io, ...@@ -473,7 +472,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
gfp_t gfp_flags = GFP_NOFS; gfp_t gfp_flags = GFP_NOFS;
retry_encrypt: retry_encrypt:
data_page = ext4_encrypt(inode, page, gfp_flags); data_page = fscrypt_encrypt_page(inode, page, gfp_flags);
if (IS_ERR(data_page)) { if (IS_ERR(data_page)) {
ret = PTR_ERR(data_page); ret = PTR_ERR(data_page);
if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) { if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) {
...@@ -511,7 +510,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io, ...@@ -511,7 +510,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
if (ret) { if (ret) {
out: out:
if (data_page) if (data_page)
ext4_restore_control_page(data_page); fscrypt_restore_control_page(data_page);
printk_ratelimited(KERN_ERR "%s: ret = %d\n", __func__, ret); printk_ratelimited(KERN_ERR "%s: ret = %d\n", __func__, ret);
redirty_page_for_writepage(wbc, page); redirty_page_for_writepage(wbc, page);
do { do {
......
...@@ -46,37 +46,6 @@ ...@@ -46,37 +46,6 @@
#include "ext4.h" #include "ext4.h"
/*
* Call ext4_decrypt on every single page, reusing the encryption
* context.
*/
static void completion_pages(struct work_struct *work)
{
#ifdef CONFIG_EXT4_FS_ENCRYPTION
struct ext4_crypto_ctx *ctx =
container_of(work, struct ext4_crypto_ctx, r.work);
struct bio *bio = ctx->r.bio;
struct bio_vec *bv;
int i;
bio_for_each_segment_all(bv, bio, i) {
struct page *page = bv->bv_page;
int ret = ext4_decrypt(page);
if (ret) {
WARN_ON_ONCE(1);
SetPageError(page);
} else
SetPageUptodate(page);
unlock_page(page);
}
ext4_release_crypto_ctx(ctx);
bio_put(bio);
#else
BUG();
#endif
}
static inline bool ext4_bio_encrypted(struct bio *bio) static inline bool ext4_bio_encrypted(struct bio *bio)
{ {
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
...@@ -104,14 +73,10 @@ static void mpage_end_io(struct bio *bio) ...@@ -104,14 +73,10 @@ static void mpage_end_io(struct bio *bio)
int i; int i;
if (ext4_bio_encrypted(bio)) { if (ext4_bio_encrypted(bio)) {
struct ext4_crypto_ctx *ctx = bio->bi_private;
if (bio->bi_error) { if (bio->bi_error) {
ext4_release_crypto_ctx(ctx); fscrypt_release_ctx(bio->bi_private);
} else { } else {
INIT_WORK(&ctx->r.work, completion_pages); fscrypt_decrypt_bio_pages(bio->bi_private, bio);
ctx->r.bio = bio;
queue_work(ext4_read_workqueue, &ctx->r.work);
return; return;
} }
} }
...@@ -135,7 +100,6 @@ int ext4_mpage_readpages(struct address_space *mapping, ...@@ -135,7 +100,6 @@ int ext4_mpage_readpages(struct address_space *mapping,
unsigned nr_pages) unsigned nr_pages)
{ {
struct bio *bio = NULL; struct bio *bio = NULL;
unsigned page_idx;
sector_t last_block_in_bio = 0; sector_t last_block_in_bio = 0;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
...@@ -157,7 +121,7 @@ int ext4_mpage_readpages(struct address_space *mapping, ...@@ -157,7 +121,7 @@ int ext4_mpage_readpages(struct address_space *mapping,
map.m_len = 0; map.m_len = 0;
map.m_flags = 0; map.m_flags = 0;
for (page_idx = 0; nr_pages; page_idx++, nr_pages--) { for (; nr_pages; nr_pages--) {
int fully_mapped = 1; int fully_mapped = 1;
unsigned first_hole = blocks_per_page; unsigned first_hole = blocks_per_page;
...@@ -275,11 +239,11 @@ int ext4_mpage_readpages(struct address_space *mapping, ...@@ -275,11 +239,11 @@ int ext4_mpage_readpages(struct address_space *mapping,
bio = NULL; bio = NULL;
} }
if (bio == NULL) { if (bio == NULL) {
struct ext4_crypto_ctx *ctx = NULL; struct fscrypt_ctx *ctx = NULL;
if (ext4_encrypted_inode(inode) && if (ext4_encrypted_inode(inode) &&
S_ISREG(inode->i_mode)) { S_ISREG(inode->i_mode)) {
ctx = ext4_get_crypto_ctx(inode, GFP_NOFS); ctx = fscrypt_get_ctx(inode, GFP_NOFS);
if (IS_ERR(ctx)) if (IS_ERR(ctx))
goto set_error_page; goto set_error_page;
} }
...@@ -287,7 +251,7 @@ int ext4_mpage_readpages(struct address_space *mapping, ...@@ -287,7 +251,7 @@ int ext4_mpage_readpages(struct address_space *mapping,
min_t(int, nr_pages, BIO_MAX_PAGES)); min_t(int, nr_pages, BIO_MAX_PAGES));
if (!bio) { if (!bio) {
if (ctx) if (ctx)
ext4_release_crypto_ctx(ctx); fscrypt_release_ctx(ctx);
goto set_error_page; goto set_error_page;
} }
bio->bi_bdev = bdev; bio->bi_bdev = bdev;
......
...@@ -945,9 +945,6 @@ static struct inode *ext4_alloc_inode(struct super_block *sb) ...@@ -945,9 +945,6 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
ei->i_datasync_tid = 0; ei->i_datasync_tid = 0;
atomic_set(&ei->i_unwritten, 0); atomic_set(&ei->i_unwritten, 0);
INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work); INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
#ifdef CONFIG_EXT4_FS_ENCRYPTION
ei->i_crypt_info = NULL;
#endif
return &ei->vfs_inode; return &ei->vfs_inode;
} }
...@@ -1026,8 +1023,7 @@ void ext4_clear_inode(struct inode *inode) ...@@ -1026,8 +1023,7 @@ void ext4_clear_inode(struct inode *inode)
EXT4_I(inode)->jinode = NULL; EXT4_I(inode)->jinode = NULL;
} }
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
if (EXT4_I(inode)->i_crypt_info) fscrypt_put_encryption_info(inode, NULL);
ext4_free_encryption_info(inode, EXT4_I(inode)->i_crypt_info);
#endif #endif
} }
...@@ -1094,6 +1090,90 @@ static int bdev_try_to_free_page(struct super_block *sb, struct page *page, ...@@ -1094,6 +1090,90 @@ static int bdev_try_to_free_page(struct super_block *sb, struct page *page,
return try_to_free_buffers(page); return try_to_free_buffers(page);
} }
#ifdef CONFIG_EXT4_FS_ENCRYPTION
static int ext4_get_context(struct inode *inode, void *ctx, size_t len)
{
return ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ctx, len);
}
static int ext4_key_prefix(struct inode *inode, u8 **key)
{
*key = EXT4_SB(inode->i_sb)->key_prefix;
return EXT4_SB(inode->i_sb)->key_prefix_size;
}
static int ext4_prepare_context(struct inode *inode)
{
return ext4_convert_inline_data(inode);
}
static int ext4_set_context(struct inode *inode, const void *ctx, size_t len,
void *fs_data)
{
handle_t *handle;
int res, res2;
/* fs_data is null when internally used. */
if (fs_data) {
res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ctx,
len, 0);
if (!res) {
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
ext4_clear_inode_state(inode,
EXT4_STATE_MAY_INLINE_DATA);
}
return res;
}
handle = ext4_journal_start(inode, EXT4_HT_MISC,
ext4_jbd2_credits_xattr(inode));
if (IS_ERR(handle))
return PTR_ERR(handle);
res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ctx,
len, 0);
if (!res) {
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
res = ext4_mark_inode_dirty(handle, inode);
if (res)
EXT4_ERROR_INODE(inode, "Failed to mark inode dirty");
}
res2 = ext4_journal_stop(handle);
if (!res)
res = res2;
return res;
}
static int ext4_dummy_context(struct inode *inode)
{
return DUMMY_ENCRYPTION_ENABLED(EXT4_SB(inode->i_sb));
}
static unsigned ext4_max_namelen(struct inode *inode)
{
return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize :
EXT4_NAME_LEN;
}
static struct fscrypt_operations ext4_cryptops = {
.get_context = ext4_get_context,
.key_prefix = ext4_key_prefix,
.prepare_context = ext4_prepare_context,
.set_context = ext4_set_context,
.dummy_context = ext4_dummy_context,
.is_encrypted = ext4_encrypted_inode,
.empty_dir = ext4_empty_dir,
.max_namelen = ext4_max_namelen,
};
#else
static struct fscrypt_operations ext4_cryptops = {
.is_encrypted = ext4_encrypted_inode,
};
#endif
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
static char *quotatypes[] = INITQFNAMES; static char *quotatypes[] = INITQFNAMES;
#define QTYPE2NAME(t) (quotatypes[t]) #define QTYPE2NAME(t) (quotatypes[t])
...@@ -2068,23 +2148,25 @@ static int ext4_fill_flex_info(struct super_block *sb) ...@@ -2068,23 +2148,25 @@ static int ext4_fill_flex_info(struct super_block *sb)
static __le16 ext4_group_desc_csum(struct super_block *sb, __u32 block_group, static __le16 ext4_group_desc_csum(struct super_block *sb, __u32 block_group,
struct ext4_group_desc *gdp) struct ext4_group_desc *gdp)
{ {
int offset; int offset = offsetof(struct ext4_group_desc, bg_checksum);
__u16 crc = 0; __u16 crc = 0;
__le32 le_group = cpu_to_le32(block_group); __le32 le_group = cpu_to_le32(block_group);
struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb);
if (ext4_has_metadata_csum(sbi->s_sb)) { if (ext4_has_metadata_csum(sbi->s_sb)) {
/* Use new metadata_csum algorithm */ /* Use new metadata_csum algorithm */
__le16 save_csum;
__u32 csum32; __u32 csum32;
__u16 dummy_csum = 0;
save_csum = gdp->bg_checksum;
gdp->bg_checksum = 0;
csum32 = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&le_group, csum32 = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&le_group,
sizeof(le_group)); sizeof(le_group));
csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp, csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp, offset);
sbi->s_desc_size); csum32 = ext4_chksum(sbi, csum32, (__u8 *)&dummy_csum,
gdp->bg_checksum = save_csum; sizeof(dummy_csum));
offset += sizeof(dummy_csum);
if (offset < sbi->s_desc_size)
csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp + offset,
sbi->s_desc_size - offset);
crc = csum32 & 0xFFFF; crc = csum32 & 0xFFFF;
goto out; goto out;
...@@ -2094,8 +2176,6 @@ static __le16 ext4_group_desc_csum(struct super_block *sb, __u32 block_group, ...@@ -2094,8 +2176,6 @@ static __le16 ext4_group_desc_csum(struct super_block *sb, __u32 block_group,
if (!ext4_has_feature_gdt_csum(sb)) if (!ext4_has_feature_gdt_csum(sb))
return 0; return 0;
offset = offsetof(struct ext4_group_desc, bg_checksum);
crc = crc16(~0, sbi->s_es->s_uuid, sizeof(sbi->s_es->s_uuid)); crc = crc16(~0, sbi->s_es->s_uuid, sizeof(sbi->s_es->s_uuid));
crc = crc16(crc, (__u8 *)&le_group, sizeof(le_group)); crc = crc16(crc, (__u8 *)&le_group, sizeof(le_group));
crc = crc16(crc, (__u8 *)gdp, offset); crc = crc16(crc, (__u8 *)gdp, offset);
...@@ -2278,6 +2358,16 @@ static void ext4_orphan_cleanup(struct super_block *sb, ...@@ -2278,6 +2358,16 @@ static void ext4_orphan_cleanup(struct super_block *sb,
while (es->s_last_orphan) { while (es->s_last_orphan) {
struct inode *inode; struct inode *inode;
/*
* We may have encountered an error during cleanup; if
* so, skip the rest.
*/
if (EXT4_SB(sb)->s_mount_state & EXT4_ERROR_FS) {
jbd_debug(1, "Skipping orphan recovery on fs with errors.\n");
es->s_last_orphan = 0;
break;
}
inode = ext4_orphan_get(sb, le32_to_cpu(es->s_last_orphan)); inode = ext4_orphan_get(sb, le32_to_cpu(es->s_last_orphan));
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
es->s_last_orphan = 0; es->s_last_orphan = 0;
...@@ -3416,6 +3506,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3416,6 +3506,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
goto failed_mount; goto failed_mount;
} }
if (le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) > (blocksize / 4)) {
ext4_msg(sb, KERN_ERR,
"Number of reserved GDT blocks insanely large: %d",
le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks));
goto failed_mount;
}
if (sbi->s_mount_opt & EXT4_MOUNT_DAX) { if (sbi->s_mount_opt & EXT4_MOUNT_DAX) {
err = bdev_dax_supported(sb, blocksize); err = bdev_dax_supported(sb, blocksize);
if (err) if (err)
...@@ -3686,6 +3783,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3686,6 +3783,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
sb->s_op = &ext4_sops; sb->s_op = &ext4_sops;
sb->s_export_op = &ext4_export_ops; sb->s_export_op = &ext4_export_ops;
sb->s_xattr = ext4_xattr_handlers; sb->s_xattr = ext4_xattr_handlers;
sb->s_cop = &ext4_cryptops;
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
sb->dq_op = &ext4_quota_operations; sb->dq_op = &ext4_quota_operations;
if (ext4_has_feature_quota(sb)) if (ext4_has_feature_quota(sb))
...@@ -3996,6 +4094,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3996,6 +4094,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10); ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10);
kfree(orig_data); kfree(orig_data);
#ifdef CONFIG_EXT4_FS_ENCRYPTION
memcpy(sbi->key_prefix, EXT4_KEY_DESC_PREFIX,
EXT4_KEY_DESC_PREFIX_SIZE);
sbi->key_prefix_size = EXT4_KEY_DESC_PREFIX_SIZE;
#endif
return 0; return 0;
cantfind_ext4: cantfind_ext4:
...@@ -4327,20 +4430,6 @@ static int ext4_commit_super(struct super_block *sb, int sync) ...@@ -4327,20 +4430,6 @@ static int ext4_commit_super(struct super_block *sb, int sync)
if (!sbh || block_device_ejected(sb)) if (!sbh || block_device_ejected(sb))
return error; return error;
if (buffer_write_io_error(sbh)) {
/*
* Oh, dear. A previous attempt to write the
* superblock failed. This could happen because the
* USB device was yanked out. Or it could happen to
* be a transient write error and maybe the block will
* be remapped. Nothing we can do but to retry the
* write and hope for the best.
*/
ext4_msg(sb, KERN_ERR, "previous I/O error to "
"superblock detected");
clear_buffer_write_io_error(sbh);
set_buffer_uptodate(sbh);
}
/* /*
* If the file system is mounted read-only, don't update the * If the file system is mounted read-only, don't update the
* superblock write time. This avoids updating the superblock * superblock write time. This avoids updating the superblock
...@@ -4371,7 +4460,23 @@ static int ext4_commit_super(struct super_block *sb, int sync) ...@@ -4371,7 +4460,23 @@ static int ext4_commit_super(struct super_block *sb, int sync)
&EXT4_SB(sb)->s_freeinodes_counter)); &EXT4_SB(sb)->s_freeinodes_counter));
BUFFER_TRACE(sbh, "marking dirty"); BUFFER_TRACE(sbh, "marking dirty");
ext4_superblock_csum_set(sb); ext4_superblock_csum_set(sb);
lock_buffer(sbh);
if (buffer_write_io_error(sbh)) {
/*
* Oh, dear. A previous attempt to write the
* superblock failed. This could happen because the
* USB device was yanked out. Or it could happen to
* be a transient write error and maybe the block will
* be remapped. Nothing we can do but to retry the
* write and hope for the best.
*/
ext4_msg(sb, KERN_ERR, "previous I/O error to "
"superblock detected");
clear_buffer_write_io_error(sbh);
set_buffer_uptodate(sbh);
}
mark_buffer_dirty(sbh); mark_buffer_dirty(sbh);
unlock_buffer(sbh);
if (sync) { if (sync) {
error = __sync_dirty_buffer(sbh, error = __sync_dirty_buffer(sbh,
test_opt(sb, BARRIER) ? WRITE_FUA : WRITE_SYNC); test_opt(sb, BARRIER) ? WRITE_FUA : WRITE_SYNC);
...@@ -5422,7 +5527,6 @@ static int __init ext4_init_fs(void) ...@@ -5422,7 +5527,6 @@ static int __init ext4_init_fs(void)
static void __exit ext4_exit_fs(void) static void __exit ext4_exit_fs(void)
{ {
ext4_exit_crypto();
ext4_destroy_lazyinit_thread(); ext4_destroy_lazyinit_thread();
unregister_as_ext2(); unregister_as_ext2();
unregister_as_ext3(); unregister_as_ext3();
......
...@@ -22,23 +22,22 @@ ...@@ -22,23 +22,22 @@
#include "ext4.h" #include "ext4.h"
#include "xattr.h" #include "xattr.h"
#ifdef CONFIG_EXT4_FS_ENCRYPTION
static const char *ext4_encrypted_get_link(struct dentry *dentry, static const char *ext4_encrypted_get_link(struct dentry *dentry,
struct inode *inode, struct inode *inode,
struct delayed_call *done) struct delayed_call *done)
{ {
struct page *cpage = NULL; struct page *cpage = NULL;
char *caddr, *paddr = NULL; char *caddr, *paddr = NULL;
struct ext4_str cstr, pstr; struct fscrypt_str cstr, pstr;
struct ext4_encrypted_symlink_data *sd; struct fscrypt_symlink_data *sd;
loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
int res; int res;
u32 plen, max_size = inode->i_sb->s_blocksize; u32 max_size = inode->i_sb->s_blocksize;
if (!dentry) if (!dentry)
return ERR_PTR(-ECHILD); return ERR_PTR(-ECHILD);
res = ext4_get_encryption_info(inode); res = fscrypt_get_encryption_info(inode);
if (res) if (res)
return ERR_PTR(res); return ERR_PTR(res);
...@@ -54,30 +53,27 @@ static const char *ext4_encrypted_get_link(struct dentry *dentry, ...@@ -54,30 +53,27 @@ static const char *ext4_encrypted_get_link(struct dentry *dentry,
} }
/* Symlink is encrypted */ /* Symlink is encrypted */
sd = (struct ext4_encrypted_symlink_data *)caddr; sd = (struct fscrypt_symlink_data *)caddr;
cstr.name = sd->encrypted_path; cstr.name = sd->encrypted_path;
cstr.len = le16_to_cpu(sd->len); cstr.len = le16_to_cpu(sd->len);
if ((cstr.len + if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) {
sizeof(struct ext4_encrypted_symlink_data) - 1) >
max_size) {
/* Symlink data on the disk is corrupted */ /* Symlink data on the disk is corrupted */
res = -EFSCORRUPTED; res = -EFSCORRUPTED;
goto errout; goto errout;
} }
plen = (cstr.len < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2) ?
EXT4_FNAME_CRYPTO_DIGEST_SIZE*2 : cstr.len; res = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
paddr = kmalloc(plen + 1, GFP_NOFS); if (res)
if (!paddr) {
res = -ENOMEM;
goto errout; goto errout;
}
pstr.name = paddr; res = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
pstr.len = plen;
res = _ext4_fname_disk_to_usr(inode, NULL, &cstr, &pstr);
if (res < 0) if (res < 0)
goto errout; goto errout;
paddr = pstr.name;
/* Null-terminate the name */ /* Null-terminate the name */
if (res <= plen) if (res <= pstr.len)
paddr[res] = '\0'; paddr[res] = '\0';
if (cpage) if (cpage)
put_page(cpage); put_page(cpage);
...@@ -99,7 +95,6 @@ const struct inode_operations ext4_encrypted_symlink_inode_operations = { ...@@ -99,7 +95,6 @@ const struct inode_operations ext4_encrypted_symlink_inode_operations = {
.listxattr = ext4_listxattr, .listxattr = ext4_listxattr,
.removexattr = generic_removexattr, .removexattr = generic_removexattr,
}; };
#endif
const struct inode_operations ext4_symlink_inode_operations = { const struct inode_operations ext4_symlink_inode_operations = {
.readlink = generic_readlink, .readlink = generic_readlink,
......
...@@ -121,17 +121,18 @@ static __le32 ext4_xattr_block_csum(struct inode *inode, ...@@ -121,17 +121,18 @@ static __le32 ext4_xattr_block_csum(struct inode *inode,
{ {
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
__u32 csum; __u32 csum;
__le32 save_csum;
__le64 dsk_block_nr = cpu_to_le64(block_nr); __le64 dsk_block_nr = cpu_to_le64(block_nr);
__u32 dummy_csum = 0;
int offset = offsetof(struct ext4_xattr_header, h_checksum);
save_csum = hdr->h_checksum;
hdr->h_checksum = 0;
csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&dsk_block_nr, csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&dsk_block_nr,
sizeof(dsk_block_nr)); sizeof(dsk_block_nr));
csum = ext4_chksum(sbi, csum, (__u8 *)hdr, csum = ext4_chksum(sbi, csum, (__u8 *)hdr, offset);
EXT4_BLOCK_SIZE(inode->i_sb)); csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum));
offset += sizeof(dummy_csum);
csum = ext4_chksum(sbi, csum, (__u8 *)hdr + offset,
EXT4_BLOCK_SIZE(inode->i_sb) - offset);
hdr->h_checksum = save_csum;
return cpu_to_le32(csum); return cpu_to_le32(csum);
} }
......
...@@ -124,7 +124,7 @@ static int journal_submit_commit_record(journal_t *journal, ...@@ -124,7 +124,7 @@ static int journal_submit_commit_record(journal_t *journal,
struct commit_header *tmp; struct commit_header *tmp;
struct buffer_head *bh; struct buffer_head *bh;
int ret; int ret;
struct timespec now = current_kernel_time(); struct timespec64 now = current_kernel_time64();
*cbh = NULL; *cbh = NULL;
......
...@@ -691,6 +691,7 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid) ...@@ -691,6 +691,7 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid)
{ {
int err = 0; int err = 0;
jbd2_might_wait_for_commit(journal);
read_lock(&journal->j_state_lock); read_lock(&journal->j_state_lock);
#ifdef CONFIG_JBD2_DEBUG #ifdef CONFIG_JBD2_DEBUG
if (!tid_geq(journal->j_commit_request, tid)) { if (!tid_geq(journal->j_commit_request, tid)) {
...@@ -1091,6 +1092,7 @@ static void jbd2_stats_proc_exit(journal_t *journal) ...@@ -1091,6 +1092,7 @@ static void jbd2_stats_proc_exit(journal_t *journal)
static journal_t * journal_init_common (void) static journal_t * journal_init_common (void)
{ {
static struct lock_class_key jbd2_trans_commit_key;
journal_t *journal; journal_t *journal;
int err; int err;
...@@ -1126,6 +1128,9 @@ static journal_t * journal_init_common (void) ...@@ -1126,6 +1128,9 @@ static journal_t * journal_init_common (void)
spin_lock_init(&journal->j_history_lock); spin_lock_init(&journal->j_history_lock);
lockdep_init_map(&journal->j_trans_commit_map, "jbd2_handle",
&jbd2_trans_commit_key, 0);
return journal; return journal;
} }
......
...@@ -182,6 +182,8 @@ static int add_transaction_credits(journal_t *journal, int blocks, ...@@ -182,6 +182,8 @@ static int add_transaction_credits(journal_t *journal, int blocks,
int needed; int needed;
int total = blocks + rsv_blocks; int total = blocks + rsv_blocks;
jbd2_might_wait_for_commit(journal);
/* /*
* If the current transaction is locked down for commit, wait * If the current transaction is locked down for commit, wait
* for the lock to be released. * for the lock to be released.
...@@ -382,13 +384,11 @@ static int start_this_handle(journal_t *journal, handle_t *handle, ...@@ -382,13 +384,11 @@ static int start_this_handle(journal_t *journal, handle_t *handle,
read_unlock(&journal->j_state_lock); read_unlock(&journal->j_state_lock);
current->journal_info = handle; current->journal_info = handle;
lock_map_acquire(&handle->h_lockdep_map); rwsem_acquire_read(&journal->j_trans_commit_map, 0, 0, _THIS_IP_);
jbd2_journal_free_transaction(new_transaction); jbd2_journal_free_transaction(new_transaction);
return 0; return 0;
} }
static struct lock_class_key jbd2_handle_key;
/* Allocate a new handle. This should probably be in a slab... */ /* Allocate a new handle. This should probably be in a slab... */
static handle_t *new_handle(int nblocks) static handle_t *new_handle(int nblocks)
{ {
...@@ -398,9 +398,6 @@ static handle_t *new_handle(int nblocks) ...@@ -398,9 +398,6 @@ static handle_t *new_handle(int nblocks)
handle->h_buffer_credits = nblocks; handle->h_buffer_credits = nblocks;
handle->h_ref = 1; handle->h_ref = 1;
lockdep_init_map(&handle->h_lockdep_map, "jbd2_handle",
&jbd2_handle_key, 0);
return handle; return handle;
} }
...@@ -672,7 +669,7 @@ int jbd2__journal_restart(handle_t *handle, int nblocks, gfp_t gfp_mask) ...@@ -672,7 +669,7 @@ int jbd2__journal_restart(handle_t *handle, int nblocks, gfp_t gfp_mask)
if (need_to_start) if (need_to_start)
jbd2_log_start_commit(journal, tid); jbd2_log_start_commit(journal, tid);
lock_map_release(&handle->h_lockdep_map); rwsem_release(&journal->j_trans_commit_map, 1, _THIS_IP_);
handle->h_buffer_credits = nblocks; handle->h_buffer_credits = nblocks;
ret = start_this_handle(journal, handle, gfp_mask); ret = start_this_handle(journal, handle, gfp_mask);
return ret; return ret;
...@@ -700,6 +697,8 @@ void jbd2_journal_lock_updates(journal_t *journal) ...@@ -700,6 +697,8 @@ void jbd2_journal_lock_updates(journal_t *journal)
{ {
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
jbd2_might_wait_for_commit(journal);
write_lock(&journal->j_state_lock); write_lock(&journal->j_state_lock);
++journal->j_barrier_count; ++journal->j_barrier_count;
...@@ -1750,11 +1749,11 @@ int jbd2_journal_stop(handle_t *handle) ...@@ -1750,11 +1749,11 @@ int jbd2_journal_stop(handle_t *handle)
wake_up(&journal->j_wait_transaction_locked); wake_up(&journal->j_wait_transaction_locked);
} }
rwsem_release(&journal->j_trans_commit_map, 1, _THIS_IP_);
if (wait_for_commit) if (wait_for_commit)
err = jbd2_log_wait_commit(journal, tid); err = jbd2_log_wait_commit(journal, tid);
lock_map_release(&handle->h_lockdep_map);
if (handle->h_rsv_handle) if (handle->h_rsv_handle)
jbd2_journal_free_reserved(handle->h_rsv_handle); jbd2_journal_free_reserved(handle->h_rsv_handle);
free_and_exit: free_and_exit:
......
...@@ -491,10 +491,6 @@ struct jbd2_journal_handle ...@@ -491,10 +491,6 @@ struct jbd2_journal_handle
unsigned long h_start_jiffies; unsigned long h_start_jiffies;
unsigned int h_requested_credits; unsigned int h_requested_credits;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map h_lockdep_map;
#endif
}; };
...@@ -793,6 +789,7 @@ jbd2_time_diff(unsigned long start, unsigned long end) ...@@ -793,6 +789,7 @@ jbd2_time_diff(unsigned long start, unsigned long end)
* @j_proc_entry: procfs entry for the jbd statistics directory * @j_proc_entry: procfs entry for the jbd statistics directory
* @j_stats: Overall statistics * @j_stats: Overall statistics
* @j_private: An opaque pointer to fs-private information. * @j_private: An opaque pointer to fs-private information.
* @j_trans_commit_map: Lockdep entity to track transaction commit dependencies
*/ */
struct journal_s struct journal_s
...@@ -1035,8 +1032,26 @@ struct journal_s ...@@ -1035,8 +1032,26 @@ struct journal_s
/* Precomputed journal UUID checksum for seeding other checksums */ /* Precomputed journal UUID checksum for seeding other checksums */
__u32 j_csum_seed; __u32 j_csum_seed;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
/*
* Lockdep entity to track transaction commit dependencies. Handles
* hold this "lock" for read, when we wait for commit, we acquire the
* "lock" for writing. This matches the properties of jbd2 journalling
* where the running transaction has to wait for all handles to be
* dropped to commit that transaction and also acquiring a handle may
* require transaction commit to finish.
*/
struct lockdep_map j_trans_commit_map;
#endif
}; };
#define jbd2_might_wait_for_commit(j) \
do { \
rwsem_acquire(&j->j_trans_commit_map, 0, 0, _THIS_IP_); \
rwsem_release(&j->j_trans_commit_map, 1, _THIS_IP_); \
} while (0)
/* journal feature predicate functions */ /* journal feature predicate functions */
#define JBD2_FEATURE_COMPAT_FUNCS(name, flagname) \ #define JBD2_FEATURE_COMPAT_FUNCS(name, flagname) \
static inline bool jbd2_has_feature_##name(journal_t *j) \ static inline bool jbd2_has_feature_##name(journal_t *j) \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment