f2fs/fscrypt: catch up to v4.12
Applied f2fs-related patches only, and tried to exclude other layers' changes. - fs/f2fs c6b1e36c8fa0 Merge branch 'for-4.13/block' of git://git.kernel.dk/linux-block 81e3e044897b Merge tag 'uuid-for-4.13' of git://git.infradead.org/users/hch/uuid fdd050b5b3c9 Merge branch 'uuid-types' of bombadil.infradead.org:public_git/uuid into nvme-base 4e4cbee93d56 block: switch bios to blk_status_t d41519a69b35 crypto: Work around deallocated stack frame reference gcc bug on sparc. 85787090a21e fs: switch ->s_uuid to uuid_t bf5f89463f5b Merge branch 'akpm' (patches from Andrew) 48fbfe50f1d5 fs: f2fs: use ktime_get_real_seconds for sit_info times a7c3e901a46f mm: introduce kv[mz]alloc helpers 70ef8f0d3757 Merge tag 'for-f2fs-4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs ... - fs/crypto/ 4e4cbee93d56 block: switch bios to blk_status_t 17159420a6c1 fscrypt: introduce helper function for filename matching 6b06cdee81d6 fscrypt: avoid collisions when presenting long encrypted filenames 272f98f68462 fscrypt: fix context consistency check when key(s) unavailable 9c8268def612 fscrypt: Move key structure and constants to uapi cd39e4bac111 fscrypt: remove unnecessary checks for NULL operations a643f9054c21 Merge tag 'fscrypt-for-linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/fscrypt ... - fs/ext4/ : Applied fscrypt-related changes only. Signed-off-by: Jaegeuk Kim <jaegeuk@google.com>
This commit is contained in:
parent
4d7931d727
commit
e6b120d4d0
45 changed files with 5414 additions and 2863 deletions
|
|
@ -1,6 +1,5 @@
|
|||
config FS_ENCRYPTION
|
||||
tristate "FS Encryption (Per-file encryption)"
|
||||
depends on BLOCK
|
||||
select CRYPTO
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
|
|
@ -8,9 +7,7 @@ config FS_ENCRYPTION
|
|||
select CRYPTO_XTS
|
||||
select CRYPTO_CTS
|
||||
select CRYPTO_CTR
|
||||
select CRYPTO_SHA256
|
||||
select KEYS
|
||||
select ENCRYPTED_KEYS
|
||||
help
|
||||
Enable encryption of files and directories. This
|
||||
feature is similar to ecryptfs, but it is more memory
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o
|
||||
|
||||
fscrypto-y := crypto.o fname.o policy.o keyinfo.o
|
||||
fscrypto-$(CONFIG_BLOCK) += bio.o
|
||||
|
|
|
|||
145
fs/crypto/bio.c
Normal file
145
fs/crypto/bio.c
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* This contains encryption functions for per-file encryption.
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
* Copyright (C) 2015, Motorola Mobility
|
||||
*
|
||||
* Written by Michael Halcrow, 2014.
|
||||
*
|
||||
* Filename encryption additions
|
||||
* Uday Savagaonkar, 2014
|
||||
* Encryption policy handling additions
|
||||
* Ildar Muslukhov, 2014
|
||||
* Add fscrypt_pullback_bio_page()
|
||||
* Jaegeuk Kim, 2015.
|
||||
*
|
||||
* This has not yet undergone a rigorous security audit.
|
||||
*
|
||||
* The usage of AES-XTS should conform to recommendations in NIST
|
||||
* Special Publication 800-38E and IEEE P1619/D16.
|
||||
*/
|
||||
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/namei.h>
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
/*
|
||||
* Call fscrypt_decrypt_page on every single page, reusing the encryption
|
||||
* context.
|
||||
*/
|
||||
static void completion_pages(struct work_struct *work)
|
||||
{
|
||||
struct fscrypt_ctx *ctx =
|
||||
container_of(work, struct fscrypt_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 = fscrypt_decrypt_page(page->mapping->host, page,
|
||||
PAGE_SIZE, 0, page->index);
|
||||
|
||||
if (ret) {
|
||||
WARN_ON_ONCE(1);
|
||||
SetPageError(page);
|
||||
} else {
|
||||
SetPageUptodate(page);
|
||||
}
|
||||
unlock_page(page);
|
||||
}
|
||||
fscrypt_release_ctx(ctx);
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, struct bio *bio)
|
||||
{
|
||||
INIT_WORK(&ctx->r.work, completion_pages);
|
||||
ctx->r.bio = bio;
|
||||
queue_work(fscrypt_read_workqueue, &ctx->r.work);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_decrypt_bio_pages);
|
||||
|
||||
void fscrypt_pullback_bio_page(struct page **page, bool restore)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *bounce_page;
|
||||
|
||||
/* The bounce data pages are unmapped. */
|
||||
if ((*page)->mapping)
|
||||
return;
|
||||
|
||||
/* The bounce data page is unmapped. */
|
||||
bounce_page = *page;
|
||||
ctx = (struct fscrypt_ctx *)page_private(bounce_page);
|
||||
|
||||
/* restore control page */
|
||||
*page = ctx->w.control_page;
|
||||
|
||||
if (restore)
|
||||
fscrypt_restore_control_page(bounce_page);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_pullback_bio_page);
|
||||
|
||||
int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,
|
||||
sector_t pblk, unsigned int len)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *ciphertext_page = NULL;
|
||||
struct bio *bio;
|
||||
int ret, err = 0;
|
||||
|
||||
BUG_ON(inode->i_sb->s_blocksize != PAGE_SIZE);
|
||||
|
||||
ctx = fscrypt_get_ctx(inode, GFP_NOFS);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
|
||||
ciphertext_page = fscrypt_alloc_bounce_page(ctx, GFP_NOWAIT);
|
||||
if (IS_ERR(ciphertext_page)) {
|
||||
err = PTR_ERR(ciphertext_page);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
while (len--) {
|
||||
err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk,
|
||||
ZERO_PAGE(0), ciphertext_page,
|
||||
PAGE_SIZE, 0, GFP_NOFS);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
bio = bio_alloc(GFP_NOWAIT, 1);
|
||||
if (!bio) {
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
bio->bi_bdev = inode->i_sb->s_bdev;
|
||||
bio->bi_iter.bi_sector =
|
||||
pblk << (inode->i_sb->s_blocksize_bits - 9);
|
||||
bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
|
||||
ret = bio_add_page(bio, ciphertext_page,
|
||||
inode->i_sb->s_blocksize, 0);
|
||||
if (ret != inode->i_sb->s_blocksize) {
|
||||
/* should never happen! */
|
||||
WARN_ON(1);
|
||||
bio_put(bio);
|
||||
err = -EIO;
|
||||
goto errout;
|
||||
}
|
||||
err = submit_bio_wait(bio);
|
||||
if (err == 0 && bio->bi_error)
|
||||
err = -EIO;
|
||||
bio_put(bio);
|
||||
if (err)
|
||||
goto errout;
|
||||
lblk++;
|
||||
pblk++;
|
||||
}
|
||||
err = 0;
|
||||
errout:
|
||||
fscrypt_release_ctx(ctx);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_zeroout_range);
|
||||
|
|
@ -24,10 +24,9 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
static unsigned int num_prealloc_crypto_pages = 32;
|
||||
static unsigned int num_prealloc_crypto_ctxs = 128;
|
||||
|
|
@ -44,7 +43,7 @@ static mempool_t *fscrypt_bounce_page_pool = NULL;
|
|||
static LIST_HEAD(fscrypt_free_ctxs);
|
||||
static DEFINE_SPINLOCK(fscrypt_ctx_lock);
|
||||
|
||||
static struct workqueue_struct *fscrypt_read_workqueue;
|
||||
struct workqueue_struct *fscrypt_read_workqueue;
|
||||
static DEFINE_MUTEX(fscrypt_init_mutex);
|
||||
|
||||
static struct kmem_cache *fscrypt_ctx_cachep;
|
||||
|
|
@ -63,7 +62,7 @@ void fscrypt_release_ctx(struct fscrypt_ctx *ctx)
|
|||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (ctx->flags & FS_WRITE_PATH_FL && ctx->w.bounce_page) {
|
||||
if (ctx->flags & FS_CTX_HAS_BOUNCE_BUFFER_FL && ctx->w.bounce_page) {
|
||||
mempool_free(ctx->w.bounce_page, fscrypt_bounce_page_pool);
|
||||
ctx->w.bounce_page = NULL;
|
||||
}
|
||||
|
|
@ -88,7 +87,7 @@ EXPORT_SYMBOL(fscrypt_release_ctx);
|
|||
* Return: An allocated and initialized encryption context on success; error
|
||||
* value or NULL otherwise.
|
||||
*/
|
||||
struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode, gfp_t gfp_flags)
|
||||
struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags)
|
||||
{
|
||||
struct fscrypt_ctx *ctx = NULL;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
|
|
@ -121,7 +120,7 @@ struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode, gfp_t gfp_flags)
|
|||
} else {
|
||||
ctx->flags &= ~FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
|
||||
}
|
||||
ctx->flags &= ~FS_WRITE_PATH_FL;
|
||||
ctx->flags &= ~FS_CTX_HAS_BOUNCE_BUFFER_FL;
|
||||
return ctx;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_get_ctx);
|
||||
|
|
@ -141,15 +140,10 @@ static void page_crypt_complete(struct crypto_async_request *req, int res)
|
|||
complete(&ecr->completion);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
FS_DECRYPT = 0,
|
||||
FS_ENCRYPT,
|
||||
} fscrypt_direction_t;
|
||||
|
||||
static int do_page_crypto(struct inode *inode,
|
||||
fscrypt_direction_t rw, pgoff_t index,
|
||||
struct page *src_page, struct page *dest_page,
|
||||
gfp_t gfp_flags)
|
||||
int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw,
|
||||
u64 lblk_num, struct page *src_page,
|
||||
struct page *dest_page, unsigned int len,
|
||||
unsigned int offs, gfp_t gfp_flags)
|
||||
{
|
||||
struct {
|
||||
__le64 index;
|
||||
|
|
@ -162,6 +156,8 @@ static int do_page_crypto(struct inode *inode,
|
|||
struct crypto_skcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
|
||||
BUG_ON(len == 0);
|
||||
|
||||
req = skcipher_request_alloc(tfm, gfp_flags);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
|
|
@ -175,14 +171,14 @@ static int do_page_crypto(struct inode *inode,
|
|||
page_crypt_complete, &ecr);
|
||||
|
||||
BUILD_BUG_ON(sizeof(xts_tweak) != FS_XTS_TWEAK_SIZE);
|
||||
xts_tweak.index = cpu_to_le64(index);
|
||||
xts_tweak.index = cpu_to_le64(lblk_num);
|
||||
memset(xts_tweak.padding, 0, sizeof(xts_tweak.padding));
|
||||
|
||||
sg_init_table(&dst, 1);
|
||||
sg_set_page(&dst, dest_page, PAGE_SIZE, 0);
|
||||
sg_set_page(&dst, dest_page, len, offs);
|
||||
sg_init_table(&src, 1);
|
||||
sg_set_page(&src, src_page, PAGE_SIZE, 0);
|
||||
skcipher_request_set_crypt(req, &src, &dst, PAGE_SIZE, &xts_tweak);
|
||||
sg_set_page(&src, src_page, len, offs);
|
||||
skcipher_request_set_crypt(req, &src, &dst, len, &xts_tweak);
|
||||
if (rw == FS_DECRYPT)
|
||||
res = crypto_skcipher_decrypt(req);
|
||||
else
|
||||
|
|
@ -202,53 +198,86 @@ static int do_page_crypto(struct inode *inode,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags)
|
||||
struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool, gfp_flags);
|
||||
if (ctx->w.bounce_page == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ctx->flags |= FS_WRITE_PATH_FL;
|
||||
ctx->flags |= FS_CTX_HAS_BOUNCE_BUFFER_FL;
|
||||
return ctx->w.bounce_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* fscypt_encrypt_page() - Encrypts a page
|
||||
* @inode: The inode for which the encryption should take place
|
||||
* @plaintext_page: The page to encrypt. Must be locked.
|
||||
* @gfp_flags: The gfp flag for memory allocation
|
||||
* @inode: The inode for which the encryption should take place
|
||||
* @page: The page to encrypt. Must be locked for bounce-page
|
||||
* encryption.
|
||||
* @len: Length of data to encrypt in @page and encrypted
|
||||
* data in returned page.
|
||||
* @offs: Offset of data within @page and returned
|
||||
* page holding encrypted data.
|
||||
* @lblk_num: Logical block number. This must be unique for multiple
|
||||
* calls with same inode, except when overwriting
|
||||
* previously written data.
|
||||
* @gfp_flags: The gfp flag for memory allocation
|
||||
*
|
||||
* Allocates a ciphertext page and encrypts plaintext_page into it using the ctx
|
||||
* encryption context.
|
||||
* Encrypts @page using the ctx encryption context. Performs encryption
|
||||
* either in-place or into a newly allocated bounce page.
|
||||
* Called on the page write path.
|
||||
*
|
||||
* Called on the page write path. The caller must call
|
||||
* Bounce page allocation is the default.
|
||||
* In this case, the contents of @page are encrypted and stored in an
|
||||
* allocated bounce page. @page has to be locked and the caller must call
|
||||
* fscrypt_restore_control_page() on the returned ciphertext page to
|
||||
* release the bounce buffer and the encryption context.
|
||||
*
|
||||
* Return: An allocated page with the encrypted content on success. Else, an
|
||||
* In-place encryption is used by setting the FS_CFLG_OWN_PAGES flag in
|
||||
* fscrypt_operations. Here, the input-page is returned with its content
|
||||
* encrypted.
|
||||
*
|
||||
* Return: A page with the encrypted content on success. Else, an
|
||||
* error value or NULL.
|
||||
*/
|
||||
struct page *fscrypt_encrypt_page(struct inode *inode,
|
||||
struct page *plaintext_page, gfp_t gfp_flags)
|
||||
struct page *fscrypt_encrypt_page(const struct inode *inode,
|
||||
struct page *page,
|
||||
unsigned int len,
|
||||
unsigned int offs,
|
||||
u64 lblk_num, gfp_t gfp_flags)
|
||||
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *ciphertext_page = NULL;
|
||||
struct page *ciphertext_page = page;
|
||||
int err;
|
||||
|
||||
BUG_ON(!PageLocked(plaintext_page));
|
||||
BUG_ON(len % FS_CRYPTO_BLOCK_SIZE != 0);
|
||||
|
||||
if (inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES) {
|
||||
/* with inplace-encryption we just encrypt the page */
|
||||
err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk_num, page,
|
||||
ciphertext_page, len, offs,
|
||||
gfp_flags);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
return ciphertext_page;
|
||||
}
|
||||
|
||||
BUG_ON(!PageLocked(page));
|
||||
|
||||
ctx = fscrypt_get_ctx(inode, gfp_flags);
|
||||
if (IS_ERR(ctx))
|
||||
return (struct page *)ctx;
|
||||
|
||||
/* The encryption operation will require a bounce page. */
|
||||
ciphertext_page = alloc_bounce_page(ctx, gfp_flags);
|
||||
ciphertext_page = fscrypt_alloc_bounce_page(ctx, gfp_flags);
|
||||
if (IS_ERR(ciphertext_page))
|
||||
goto errout;
|
||||
|
||||
ctx->w.control_page = plaintext_page;
|
||||
err = do_page_crypto(inode, FS_ENCRYPT, plaintext_page->index,
|
||||
plaintext_page, ciphertext_page,
|
||||
gfp_flags);
|
||||
ctx->w.control_page = page;
|
||||
err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk_num,
|
||||
page, ciphertext_page, len, offs,
|
||||
gfp_flags);
|
||||
if (err) {
|
||||
ciphertext_page = ERR_PTR(err);
|
||||
goto errout;
|
||||
|
|
@ -265,8 +294,13 @@ errout:
|
|||
EXPORT_SYMBOL(fscrypt_encrypt_page);
|
||||
|
||||
/**
|
||||
* f2crypt_decrypt_page() - Decrypts a page in-place
|
||||
* @page: The page to decrypt. Must be locked.
|
||||
* fscrypt_decrypt_page() - Decrypts a page in-place
|
||||
* @inode: The corresponding inode for the page to decrypt.
|
||||
* @page: The page to decrypt. Must be locked in case
|
||||
* it is a writeback page (FS_CFLG_OWN_PAGES unset).
|
||||
* @len: Number of bytes in @page to be decrypted.
|
||||
* @offs: Start of data in @page.
|
||||
* @lblk_num: Logical block number.
|
||||
*
|
||||
* Decrypts page in-place using the ctx encryption context.
|
||||
*
|
||||
|
|
@ -274,76 +308,17 @@ EXPORT_SYMBOL(fscrypt_encrypt_page);
|
|||
*
|
||||
* Return: Zero on success, non-zero otherwise.
|
||||
*/
|
||||
int fscrypt_decrypt_page(struct page *page)
|
||||
int fscrypt_decrypt_page(const struct inode *inode, struct page *page,
|
||||
unsigned int len, unsigned int offs, u64 lblk_num)
|
||||
{
|
||||
BUG_ON(!PageLocked(page));
|
||||
if (!(inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES))
|
||||
BUG_ON(!PageLocked(page));
|
||||
|
||||
return do_page_crypto(page->mapping->host,
|
||||
FS_DECRYPT, page->index, page, page, GFP_NOFS);
|
||||
return fscrypt_do_page_crypto(inode, FS_DECRYPT, lblk_num, page, page,
|
||||
len, offs, GFP_NOFS);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_decrypt_page);
|
||||
|
||||
int fscrypt_zeroout_range(struct inode *inode, pgoff_t lblk,
|
||||
sector_t pblk, unsigned int len)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *ciphertext_page = NULL;
|
||||
struct bio *bio;
|
||||
int ret, err = 0;
|
||||
|
||||
BUG_ON(inode->i_sb->s_blocksize != PAGE_SIZE);
|
||||
|
||||
ctx = fscrypt_get_ctx(inode, GFP_NOFS);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
|
||||
ciphertext_page = alloc_bounce_page(ctx, GFP_NOWAIT);
|
||||
if (IS_ERR(ciphertext_page)) {
|
||||
err = PTR_ERR(ciphertext_page);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
while (len--) {
|
||||
err = do_page_crypto(inode, FS_ENCRYPT, lblk,
|
||||
ZERO_PAGE(0), ciphertext_page,
|
||||
GFP_NOFS);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
bio = bio_alloc(GFP_NOWAIT, 1);
|
||||
if (!bio) {
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
bio->bi_bdev = inode->i_sb->s_bdev;
|
||||
bio->bi_iter.bi_sector =
|
||||
pblk << (inode->i_sb->s_blocksize_bits - 9);
|
||||
bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
|
||||
ret = bio_add_page(bio, ciphertext_page,
|
||||
inode->i_sb->s_blocksize, 0);
|
||||
if (ret != inode->i_sb->s_blocksize) {
|
||||
/* should never happen! */
|
||||
WARN_ON(1);
|
||||
bio_put(bio);
|
||||
err = -EIO;
|
||||
goto errout;
|
||||
}
|
||||
err = submit_bio_wait(bio);
|
||||
if ((err == 0) && bio->bi_error)
|
||||
err = -EIO;
|
||||
bio_put(bio);
|
||||
if (err)
|
||||
goto errout;
|
||||
lblk++;
|
||||
pblk++;
|
||||
}
|
||||
err = 0;
|
||||
errout:
|
||||
fscrypt_release_ctx(ctx);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_zeroout_range);
|
||||
|
||||
/*
|
||||
* Validate dentries for encrypted directories to make sure we aren't
|
||||
* potentially caching stale data after a key has been added or
|
||||
|
|
@ -392,63 +367,6 @@ const struct dentry_operations fscrypt_d_ops = {
|
|||
};
|
||||
EXPORT_SYMBOL(fscrypt_d_ops);
|
||||
|
||||
/*
|
||||
* Call fscrypt_decrypt_page on every single page, reusing the encryption
|
||||
* context.
|
||||
*/
|
||||
static void completion_pages(struct work_struct *work)
|
||||
{
|
||||
struct fscrypt_ctx *ctx =
|
||||
container_of(work, struct fscrypt_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 = fscrypt_decrypt_page(page);
|
||||
|
||||
if (ret) {
|
||||
WARN_ON_ONCE(1);
|
||||
SetPageError(page);
|
||||
} else {
|
||||
SetPageUptodate(page);
|
||||
}
|
||||
unlock_page(page);
|
||||
}
|
||||
fscrypt_release_ctx(ctx);
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, struct bio *bio)
|
||||
{
|
||||
INIT_WORK(&ctx->r.work, completion_pages);
|
||||
ctx->r.bio = bio;
|
||||
queue_work(fscrypt_read_workqueue, &ctx->r.work);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_decrypt_bio_pages);
|
||||
|
||||
void fscrypt_pullback_bio_page(struct page **page, bool restore)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *bounce_page;
|
||||
|
||||
/* The bounce data pages are unmapped. */
|
||||
if ((*page)->mapping)
|
||||
return;
|
||||
|
||||
/* The bounce data page is unmapped. */
|
||||
bounce_page = *page;
|
||||
ctx = (struct fscrypt_ctx *)page_private(bounce_page);
|
||||
|
||||
/* restore control page */
|
||||
*page = ctx->w.control_page;
|
||||
|
||||
if (restore)
|
||||
fscrypt_restore_control_page(bounce_page);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_pullback_bio_page);
|
||||
|
||||
void fscrypt_restore_control_page(struct page *page)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
|
|
@ -474,17 +392,22 @@ static void fscrypt_destroy(void)
|
|||
|
||||
/**
|
||||
* fscrypt_initialize() - allocate major buffers for fs encryption.
|
||||
* @cop_flags: fscrypt operations flags
|
||||
*
|
||||
* We only call this when we start accessing encrypted files, since it
|
||||
* results in memory getting allocated that wouldn't otherwise be used.
|
||||
*
|
||||
* Return: Zero on success, non-zero otherwise.
|
||||
*/
|
||||
int fscrypt_initialize(void)
|
||||
int fscrypt_initialize(unsigned int cop_flags)
|
||||
{
|
||||
int i, res = -ENOMEM;
|
||||
|
||||
if (fscrypt_bounce_page_pool)
|
||||
/*
|
||||
* No need to allocate a bounce page pool if there already is one or
|
||||
* this FS won't use it.
|
||||
*/
|
||||
if (cop_flags & FS_CFLG_OWN_PAGES || fscrypt_bounce_page_pool)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&fscrypt_init_mutex);
|
||||
|
|
@ -513,7 +436,6 @@ fail:
|
|||
mutex_unlock(&fscrypt_init_mutex);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_initialize);
|
||||
|
||||
/**
|
||||
* fscrypt_init() - Set up for fs encryption.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
/**
|
||||
* fname_crypt_complete() - completion callback for filename crypto
|
||||
|
|
@ -159,6 +159,8 @@ static int fname_decrypt(struct inode *inode,
|
|||
static const char *lookup_table =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
||||
|
||||
#define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3)
|
||||
|
||||
/**
|
||||
* digest_encode() -
|
||||
*
|
||||
|
|
@ -209,7 +211,7 @@ static int digest_decode(const char *src, int len, char *dst)
|
|||
return cp - dst;
|
||||
}
|
||||
|
||||
u32 fscrypt_fname_encrypted_size(struct inode *inode, u32 ilen)
|
||||
u32 fscrypt_fname_encrypted_size(const struct inode *inode, u32 ilen)
|
||||
{
|
||||
int padding = 32;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
|
|
@ -227,14 +229,17 @@ EXPORT_SYMBOL(fscrypt_fname_encrypted_size);
|
|||
* Allocates an output buffer that is sufficient for the crypto operation
|
||||
* specified by the context and the direction.
|
||||
*/
|
||||
int fscrypt_fname_alloc_buffer(struct inode *inode,
|
||||
int fscrypt_fname_alloc_buffer(const struct inode *inode,
|
||||
u32 ilen, struct fscrypt_str *crypto_str)
|
||||
{
|
||||
unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen);
|
||||
u32 olen = fscrypt_fname_encrypted_size(inode, ilen);
|
||||
const u32 max_encoded_len =
|
||||
max_t(u32, BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE),
|
||||
1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)));
|
||||
|
||||
crypto_str->len = olen;
|
||||
if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
|
||||
olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
|
||||
olen = max(olen, max_encoded_len);
|
||||
|
||||
/*
|
||||
* Allocated buffer can hold one more character to null-terminate the
|
||||
* string
|
||||
|
|
@ -266,6 +271,10 @@ EXPORT_SYMBOL(fscrypt_fname_free_buffer);
|
|||
*
|
||||
* The caller must have allocated sufficient memory for the @oname string.
|
||||
*
|
||||
* If the key is available, we'll decrypt the disk name; otherwise, we'll encode
|
||||
* it for presentation. Short names are directly base64-encoded, while long
|
||||
* names are encoded in fscrypt_digested_name format.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||
|
|
@ -274,7 +283,7 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
|
|||
struct fscrypt_str *oname)
|
||||
{
|
||||
const struct qstr qname = FSTR_TO_QSTR(iname);
|
||||
char buf[24];
|
||||
struct fscrypt_digested_name digested_name;
|
||||
|
||||
if (fscrypt_is_dot_dotdot(&qname)) {
|
||||
oname->name[0] = '.';
|
||||
|
|
@ -289,20 +298,24 @@ int fscrypt_fname_disk_to_usr(struct inode *inode,
|
|||
if (inode->i_crypt_info)
|
||||
return fname_decrypt(inode, iname, oname);
|
||||
|
||||
if (iname->len <= FS_FNAME_CRYPTO_DIGEST_SIZE) {
|
||||
if (iname->len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) {
|
||||
oname->len = digest_encode(iname->name, iname->len,
|
||||
oname->name);
|
||||
return 0;
|
||||
}
|
||||
if (hash) {
|
||||
memcpy(buf, &hash, 4);
|
||||
memcpy(buf + 4, &minor_hash, 4);
|
||||
digested_name.hash = hash;
|
||||
digested_name.minor_hash = minor_hash;
|
||||
} else {
|
||||
memset(buf, 0, 8);
|
||||
digested_name.hash = 0;
|
||||
digested_name.minor_hash = 0;
|
||||
}
|
||||
memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16);
|
||||
memcpy(digested_name.digest,
|
||||
FSCRYPT_FNAME_DIGEST(iname->name, iname->len),
|
||||
FSCRYPT_FNAME_DIGEST_SIZE);
|
||||
oname->name[0] = '_';
|
||||
oname->len = 1 + digest_encode(buf, 24, oname->name + 1);
|
||||
oname->len = 1 + digest_encode((const char *)&digested_name,
|
||||
sizeof(digested_name), oname->name + 1);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
|
||||
|
|
@ -332,14 +345,39 @@ int fscrypt_fname_usr_to_disk(struct inode *inode,
|
|||
* in a directory. Consequently, a user space name cannot be mapped to
|
||||
* a disk-space name
|
||||
*/
|
||||
return -EACCES;
|
||||
return -ENOKEY;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_usr_to_disk);
|
||||
|
||||
/**
|
||||
* fscrypt_setup_filename() - prepare to search a possibly encrypted directory
|
||||
* @dir: the directory that will be searched
|
||||
* @iname: the user-provided filename being searched for
|
||||
* @lookup: 1 if we're allowed to proceed without the key because it's
|
||||
* ->lookup() or we're finding the dir_entry for deletion; 0 if we cannot
|
||||
* proceed without the key because we're going to create the dir_entry.
|
||||
* @fname: the filename information to be filled in
|
||||
*
|
||||
* Given a user-provided filename @iname, this function sets @fname->disk_name
|
||||
* to the name that would be stored in the on-disk directory entry, if possible.
|
||||
* If the directory is unencrypted this is simply @iname. Else, if we have the
|
||||
* directory's encryption key, then @iname is the plaintext, so we encrypt it to
|
||||
* get the disk_name.
|
||||
*
|
||||
* Else, for keyless @lookup operations, @iname is the presented ciphertext, so
|
||||
* we decode it to get either the ciphertext disk_name (for short names) or the
|
||||
* fscrypt_digested_name (for long names). Non-@lookup operations will be
|
||||
* impossible in this case, so we fail them with ENOKEY.
|
||||
*
|
||||
* If successful, fscrypt_free_filename() must be called later to clean up.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct fscrypt_name *fname)
|
||||
{
|
||||
int ret = 0, bigname = 0;
|
||||
int ret;
|
||||
int digested;
|
||||
|
||||
memset(fname, 0, sizeof(struct fscrypt_name));
|
||||
fname->usr_fname = iname;
|
||||
|
|
@ -367,31 +405,43 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
|||
return 0;
|
||||
}
|
||||
if (!lookup)
|
||||
return -EACCES;
|
||||
return -ENOKEY;
|
||||
|
||||
/*
|
||||
* We don't have the key and we are doing a lookup; decode the
|
||||
* user-supplied name
|
||||
*/
|
||||
if (iname->name[0] == '_')
|
||||
bigname = 1;
|
||||
if ((bigname && (iname->len != 33)) || (!bigname && (iname->len > 43)))
|
||||
return -ENOENT;
|
||||
if (iname->name[0] == '_') {
|
||||
if (iname->len !=
|
||||
1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name)))
|
||||
return -ENOENT;
|
||||
digested = 1;
|
||||
} else {
|
||||
if (iname->len >
|
||||
BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE))
|
||||
return -ENOENT;
|
||||
digested = 0;
|
||||
}
|
||||
|
||||
fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
|
||||
fname->crypto_buf.name =
|
||||
kmalloc(max_t(size_t, FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE,
|
||||
sizeof(struct fscrypt_digested_name)),
|
||||
GFP_KERNEL);
|
||||
if (fname->crypto_buf.name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = digest_decode(iname->name + bigname, iname->len - bigname,
|
||||
ret = digest_decode(iname->name + digested, iname->len - digested,
|
||||
fname->crypto_buf.name);
|
||||
if (ret < 0) {
|
||||
ret = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
fname->crypto_buf.len = ret;
|
||||
if (bigname) {
|
||||
memcpy(&fname->hash, fname->crypto_buf.name, 4);
|
||||
memcpy(&fname->minor_hash, fname->crypto_buf.name + 4, 4);
|
||||
if (digested) {
|
||||
const struct fscrypt_digested_name *n =
|
||||
(const void *)fname->crypto_buf.name;
|
||||
fname->hash = n->hash;
|
||||
fname->minor_hash = n->minor_hash;
|
||||
} else {
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
|
|
|
|||
90
fs/crypto/fscrypt_private.h
Normal file
90
fs/crypto/fscrypt_private.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* fscrypt_private.h
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains encryption key functions.
|
||||
*
|
||||
* Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
|
||||
*/
|
||||
|
||||
#ifndef _FSCRYPT_PRIVATE_H
|
||||
#define _FSCRYPT_PRIVATE_H
|
||||
|
||||
#include <linux/fscrypt_supp.h>
|
||||
|
||||
/* Encryption parameters */
|
||||
#define FS_XTS_TWEAK_SIZE 16
|
||||
#define FS_AES_128_ECB_KEY_SIZE 16
|
||||
#define FS_AES_256_GCM_KEY_SIZE 32
|
||||
#define FS_AES_256_CBC_KEY_SIZE 32
|
||||
#define FS_AES_256_CTS_KEY_SIZE 32
|
||||
#define FS_AES_256_XTS_KEY_SIZE 64
|
||||
|
||||
#define FS_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: Flags
|
||||
* 8 bytes: Master Key descriptor
|
||||
* 16 bytes: Encryption Key derivation nonce
|
||||
*/
|
||||
struct fscrypt_context {
|
||||
u8 format;
|
||||
u8 contents_encryption_mode;
|
||||
u8 filenames_encryption_mode;
|
||||
u8 flags;
|
||||
u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
|
||||
u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
|
||||
} __packed;
|
||||
|
||||
#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1
|
||||
|
||||
/*
|
||||
* A pointer to this structure is stored in the file system's in-core
|
||||
* representation of an inode.
|
||||
*/
|
||||
struct fscrypt_info {
|
||||
u8 ci_data_mode;
|
||||
u8 ci_filename_mode;
|
||||
u8 ci_flags;
|
||||
struct crypto_skcipher *ci_ctfm;
|
||||
u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE];
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
FS_DECRYPT = 0,
|
||||
FS_ENCRYPT,
|
||||
} fscrypt_direction_t;
|
||||
|
||||
#define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
|
||||
#define FS_CTX_HAS_BOUNCE_BUFFER_FL 0x00000002
|
||||
|
||||
struct fscrypt_completion_result {
|
||||
struct completion completion;
|
||||
int res;
|
||||
};
|
||||
|
||||
#define DECLARE_FS_COMPLETION_RESULT(ecr) \
|
||||
struct fscrypt_completion_result ecr = { \
|
||||
COMPLETION_INITIALIZER_ONSTACK((ecr).completion), 0 }
|
||||
|
||||
|
||||
/* crypto.c */
|
||||
extern int fscrypt_initialize(unsigned int cop_flags);
|
||||
extern struct workqueue_struct *fscrypt_read_workqueue;
|
||||
extern int fscrypt_do_page_crypto(const struct inode *inode,
|
||||
fscrypt_direction_t rw, u64 lblk_num,
|
||||
struct page *src_page,
|
||||
struct page *dest_page,
|
||||
unsigned int len, unsigned int offs,
|
||||
gfp_t gfp_flags);
|
||||
extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx,
|
||||
gfp_t gfp_flags);
|
||||
|
||||
#endif /* _FSCRYPT_PRIVATE_H */
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
static void derive_crypt_complete(struct crypto_async_request *req, int rc)
|
||||
{
|
||||
|
|
@ -77,26 +77,22 @@ out:
|
|||
|
||||
static int validate_user_key(struct fscrypt_info *crypt_info,
|
||||
struct fscrypt_context *ctx, u8 *raw_key,
|
||||
u8 *prefix, int prefix_size)
|
||||
const char *prefix)
|
||||
{
|
||||
u8 *full_key_descriptor;
|
||||
char *description;
|
||||
struct key *keyring_key;
|
||||
struct fscrypt_key *master_key;
|
||||
const struct user_key_payload *ukp;
|
||||
int full_key_len = prefix_size + (FS_KEY_DESCRIPTOR_SIZE * 2) + 1;
|
||||
int res;
|
||||
|
||||
full_key_descriptor = kmalloc(full_key_len, GFP_NOFS);
|
||||
if (!full_key_descriptor)
|
||||
description = kasprintf(GFP_NOFS, "%s%*phN", prefix,
|
||||
FS_KEY_DESCRIPTOR_SIZE,
|
||||
ctx->master_key_descriptor);
|
||||
if (!description)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(full_key_descriptor, prefix, prefix_size);
|
||||
sprintf(full_key_descriptor + prefix_size,
|
||||
"%*phN", FS_KEY_DESCRIPTOR_SIZE,
|
||||
ctx->master_key_descriptor);
|
||||
full_key_descriptor[full_key_len - 1] = '\0';
|
||||
keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
|
||||
kfree(full_key_descriptor);
|
||||
keyring_key = request_key(&key_type_logon, description, NULL);
|
||||
kfree(description);
|
||||
if (IS_ERR(keyring_key))
|
||||
return PTR_ERR(keyring_key);
|
||||
down_read(&keyring_key->sem);
|
||||
|
|
@ -107,7 +103,6 @@ static int validate_user_key(struct fscrypt_info *crypt_info,
|
|||
res = -ENOKEY;
|
||||
goto out;
|
||||
}
|
||||
down_read(&keyring_key->sem);
|
||||
ukp = user_key_payload_locked(keyring_key);
|
||||
if (ukp->datalen != sizeof(struct fscrypt_key)) {
|
||||
res = -EINVAL;
|
||||
|
|
@ -184,21 +179,21 @@ int fscrypt_get_encryption_info(struct inode *inode)
|
|||
if (inode->i_crypt_info)
|
||||
return 0;
|
||||
|
||||
res = fscrypt_initialize();
|
||||
res = fscrypt_initialize(inode->i_sb->s_cop->flags);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (!inode->i_sb->s_cop->get_context)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
if (res < 0) {
|
||||
if (!fscrypt_dummy_context_enabled(inode))
|
||||
if (!fscrypt_dummy_context_enabled(inode) ||
|
||||
inode->i_sb->s_cop->is_encrypted(inode))
|
||||
return res;
|
||||
/* Fake up a context for an unencrypted directory */
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
|
||||
ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
|
||||
ctx.flags = 0;
|
||||
memset(ctx.master_key_descriptor, 0x42, FS_KEY_DESCRIPTOR_SIZE);
|
||||
} else if (res != sizeof(ctx)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -233,20 +228,10 @@ int fscrypt_get_encryption_info(struct inode *inode)
|
|||
if (!raw_key)
|
||||
goto out;
|
||||
|
||||
if (fscrypt_dummy_context_enabled(inode)) {
|
||||
memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE);
|
||||
goto got_key;
|
||||
}
|
||||
|
||||
res = validate_user_key(crypt_info, &ctx, raw_key,
|
||||
FS_KEY_DESC_PREFIX, FS_KEY_DESC_PREFIX_SIZE);
|
||||
res = validate_user_key(crypt_info, &ctx, raw_key, FS_KEY_DESC_PREFIX);
|
||||
if (res && inode->i_sb->s_cop->key_prefix) {
|
||||
u8 *prefix = NULL;
|
||||
int prefix_size, res2;
|
||||
|
||||
prefix_size = inode->i_sb->s_cop->key_prefix(inode, &prefix);
|
||||
res2 = validate_user_key(crypt_info, &ctx, raw_key,
|
||||
prefix, prefix_size);
|
||||
int res2 = validate_user_key(crypt_info, &ctx, raw_key,
|
||||
inode->i_sb->s_cop->key_prefix);
|
||||
if (res2) {
|
||||
if (res2 == -ENOKEY)
|
||||
res = -ENOKEY;
|
||||
|
|
@ -255,7 +240,6 @@ int fscrypt_get_encryption_info(struct inode *inode)
|
|||
} else 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;
|
||||
|
|
|
|||
|
|
@ -10,76 +10,41 @@
|
|||
|
||||
#include <linux/random.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#include <linux/mount.h>
|
||||
|
||||
static int inode_has_encryption_context(struct inode *inode)
|
||||
{
|
||||
if (!inode->i_sb->s_cop->get_context)
|
||||
return 0;
|
||||
return (inode->i_sb->s_cop->get_context(inode, NULL, 0L) > 0);
|
||||
}
|
||||
#include "fscrypt_private.h"
|
||||
|
||||
/*
|
||||
* check whether the policy is consistent with the encryption context
|
||||
* for the inode
|
||||
* check whether an encryption policy is consistent with an encryption context
|
||||
*/
|
||||
static int is_encryption_context_consistent_with_policy(struct inode *inode,
|
||||
static bool is_encryption_context_consistent_with_policy(
|
||||
const struct fscrypt_context *ctx,
|
||||
const struct fscrypt_policy *policy)
|
||||
{
|
||||
struct fscrypt_context ctx;
|
||||
int res;
|
||||
|
||||
if (!inode->i_sb->s_cop->get_context)
|
||||
return 0;
|
||||
|
||||
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
if (res != sizeof(ctx))
|
||||
return 0;
|
||||
|
||||
return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
FS_KEY_DESCRIPTOR_SIZE) == 0 &&
|
||||
(ctx.flags == policy->flags) &&
|
||||
(ctx.contents_encryption_mode ==
|
||||
policy->contents_encryption_mode) &&
|
||||
(ctx.filenames_encryption_mode ==
|
||||
policy->filenames_encryption_mode));
|
||||
return memcmp(ctx->master_key_descriptor, policy->master_key_descriptor,
|
||||
FS_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 create_encryption_context_from_policy(struct inode *inode,
|
||||
const struct fscrypt_policy *policy)
|
||||
{
|
||||
struct fscrypt_context ctx;
|
||||
int res;
|
||||
|
||||
if (!inode->i_sb->s_cop->set_context)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (inode->i_sb->s_cop->prepare_context) {
|
||||
res = inode->i_sb->s_cop->prepare_context(inode);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
FS_KEY_DESCRIPTOR_SIZE);
|
||||
|
||||
if (!fscrypt_valid_contents_enc_mode(
|
||||
policy->contents_encryption_mode)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Invalid contents encryption mode %d\n", __func__,
|
||||
policy->contents_encryption_mode);
|
||||
policy->contents_encryption_mode))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!fscrypt_valid_filenames_enc_mode(
|
||||
policy->filenames_encryption_mode)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Invalid filenames encryption mode %d\n", __func__,
|
||||
policy->filenames_encryption_mode);
|
||||
policy->filenames_encryption_mode))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (policy->flags & ~FS_POLICY_FLAGS_VALID)
|
||||
return -EINVAL;
|
||||
|
|
@ -93,16 +58,20 @@ static int create_encryption_context_from_policy(struct inode *inode,
|
|||
return inode->i_sb->s_cop->set_context(inode, &ctx, sizeof(ctx), NULL);
|
||||
}
|
||||
|
||||
int fscrypt_process_policy(struct file *filp,
|
||||
const struct fscrypt_policy *policy)
|
||||
int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
|
||||
{
|
||||
struct fscrypt_policy policy;
|
||||
struct inode *inode = file_inode(filp);
|
||||
int ret;
|
||||
struct fscrypt_context ctx;
|
||||
|
||||
if (copy_from_user(&policy, arg, sizeof(policy)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EACCES;
|
||||
|
||||
if (policy->version != 0)
|
||||
if (policy.version != 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
|
|
@ -111,22 +80,23 @@ int fscrypt_process_policy(struct file *filp,
|
|||
|
||||
inode_lock(inode);
|
||||
|
||||
if (!inode_has_encryption_context(inode)) {
|
||||
ret = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
if (ret == -ENODATA) {
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
ret = -EINVAL;
|
||||
else if (!inode->i_sb->s_cop->empty_dir)
|
||||
ret = -EOPNOTSUPP;
|
||||
ret = -ENOTDIR;
|
||||
else if (!inode->i_sb->s_cop->empty_dir(inode))
|
||||
ret = -ENOTEMPTY;
|
||||
else
|
||||
ret = create_encryption_context_from_policy(inode,
|
||||
policy);
|
||||
} else if (!is_encryption_context_consistent_with_policy(inode,
|
||||
policy)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Policy inconsistent with encryption context\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
&policy);
|
||||
} else if (ret == sizeof(ctx) &&
|
||||
is_encryption_context_consistent_with_policy(&ctx,
|
||||
&policy)) {
|
||||
/* The file already uses the same encryption policy. */
|
||||
ret = 0;
|
||||
} else if (ret >= 0 || ret == -ERANGE) {
|
||||
/* The file already uses a different encryption policy. */
|
||||
ret = -EEXIST;
|
||||
}
|
||||
|
||||
inode_unlock(inode);
|
||||
|
|
@ -134,32 +104,38 @@ int fscrypt_process_policy(struct file *filp,
|
|||
mnt_drop_write_file(filp);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_process_policy);
|
||||
EXPORT_SYMBOL(fscrypt_ioctl_set_policy);
|
||||
|
||||
int fscrypt_get_policy(struct inode *inode, struct fscrypt_policy *policy)
|
||||
int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct fscrypt_context ctx;
|
||||
struct fscrypt_policy policy;
|
||||
int res;
|
||||
|
||||
if (!inode->i_sb->s_cop->get_context ||
|
||||
!inode->i_sb->s_cop->is_encrypted(inode))
|
||||
if (!inode->i_sb->s_cop->is_encrypted(inode))
|
||||
return -ENODATA;
|
||||
|
||||
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
if (res < 0 && res != -ERANGE)
|
||||
return res;
|
||||
if (res != sizeof(ctx))
|
||||
return -ENODATA;
|
||||
return -EINVAL;
|
||||
if (ctx.format != FS_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,
|
||||
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,
|
||||
FS_KEY_DESCRIPTOR_SIZE);
|
||||
|
||||
if (copy_to_user(arg, &policy, sizeof(policy)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_get_policy);
|
||||
EXPORT_SYMBOL(fscrypt_ioctl_get_policy);
|
||||
|
||||
/**
|
||||
* fscrypt_has_permitted_context() - is a file's encryption policy permitted
|
||||
|
|
@ -258,9 +234,9 @@ EXPORT_SYMBOL(fscrypt_has_permitted_context);
|
|||
* @parent: Parent inode from which the context is inherited.
|
||||
* @child: Child inode that inherits the context from @parent.
|
||||
* @fs_data: private data given by FS.
|
||||
* @preload: preload child i_crypt_info
|
||||
* @preload: preload child i_crypt_info if true
|
||||
*
|
||||
* Return: Zero on success, non-zero otherwise
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int fscrypt_inherit_context(struct inode *parent, struct inode *child,
|
||||
void *fs_data, bool preload)
|
||||
|
|
@ -269,9 +245,6 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
|
|||
struct fscrypt_info *ci;
|
||||
int res;
|
||||
|
||||
if (!parent->i_sb->s_cop->set_context)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
res = fscrypt_get_encryption_info(parent);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
|
@ -281,19 +254,11 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
|
|||
return -ENOKEY;
|
||||
|
||||
ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
if (fscrypt_dummy_context_enabled(parent)) {
|
||||
ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
|
||||
ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
|
||||
ctx.flags = 0;
|
||||
memset(ctx.master_key_descriptor, 0x42, FS_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,
|
||||
FS_KEY_DESCRIPTOR_SIZE);
|
||||
}
|
||||
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,
|
||||
FS_KEY_DESCRIPTOR_SIZE);
|
||||
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
|
||||
res = parent->i_sb->s_cop->set_context(child, &ctx,
|
||||
sizeof(ctx), fs_data);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,11 @@
|
|||
#include <linux/percpu_counter.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
#include <linux/fscrypt_supp.h>
|
||||
#else
|
||||
#include <linux/fscrypt_notsupp.h>
|
||||
#endif
|
||||
#include <linux/falloc.h>
|
||||
#include <linux/percpu-rwsem.h>
|
||||
#ifdef __KERNEL__
|
||||
|
|
@ -1342,11 +1346,6 @@ struct ext4_super_block {
|
|||
/* Number of quota types we support */
|
||||
#define EXT4_MAXQUOTAS 3
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
#define EXT4_KEY_DESC_PREFIX "ext4:"
|
||||
#define EXT4_KEY_DESC_PREFIX_SIZE 5
|
||||
#endif
|
||||
|
||||
/*
|
||||
* fourth extended-fs super-block data in memory
|
||||
*/
|
||||
|
|
@ -1516,12 +1515,6 @@ struct ext4_sb_info {
|
|||
|
||||
/* Barrier between changing inodes' journal flags and writepages ops. */
|
||||
struct percpu_rw_semaphore s_journal_flag_rwsem;
|
||||
|
||||
/* Encryption support */
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
u8 key_prefix[EXT4_KEY_DESC_PREFIX_SIZE];
|
||||
u8 key_prefix_size;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
|
||||
|
|
@ -2277,11 +2270,6 @@ 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 *);
|
||||
|
||||
static inline int ext4_sb_has_crypto(struct super_block *sb)
|
||||
{
|
||||
return ext4_has_feature_encrypt(sb);
|
||||
}
|
||||
|
||||
static inline bool ext4_encrypted_inode(struct inode *inode)
|
||||
{
|
||||
return ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
|
|
@ -2330,28 +2318,6 @@ static inline int ext4_fname_setup_filename(struct inode *dir,
|
|||
}
|
||||
static inline void ext4_fname_free_filename(struct ext4_filename *fname) { }
|
||||
|
||||
#define fscrypt_set_d_op(i)
|
||||
#define fscrypt_get_ctx fscrypt_notsupp_get_ctx
|
||||
#define fscrypt_release_ctx fscrypt_notsupp_release_ctx
|
||||
#define fscrypt_encrypt_page fscrypt_notsupp_encrypt_page
|
||||
#define fscrypt_decrypt_page fscrypt_notsupp_decrypt_page
|
||||
#define fscrypt_decrypt_bio_pages fscrypt_notsupp_decrypt_bio_pages
|
||||
#define fscrypt_pullback_bio_page fscrypt_notsupp_pullback_bio_page
|
||||
#define fscrypt_restore_control_page fscrypt_notsupp_restore_control_page
|
||||
#define fscrypt_zeroout_range fscrypt_notsupp_zeroout_range
|
||||
#define fscrypt_process_policy fscrypt_notsupp_process_policy
|
||||
#define fscrypt_get_policy fscrypt_notsupp_get_policy
|
||||
#define fscrypt_has_permitted_context fscrypt_notsupp_has_permitted_context
|
||||
#define fscrypt_inherit_context fscrypt_notsupp_inherit_context
|
||||
#define fscrypt_get_encryption_info fscrypt_notsupp_get_encryption_info
|
||||
#define fscrypt_put_encryption_info fscrypt_notsupp_put_encryption_info
|
||||
#define fscrypt_setup_filename fscrypt_notsupp_setup_filename
|
||||
#define fscrypt_free_filename fscrypt_notsupp_free_filename
|
||||
#define fscrypt_fname_encrypted_size fscrypt_notsupp_fname_encrypted_size
|
||||
#define fscrypt_fname_alloc_buffer fscrypt_notsupp_fname_alloc_buffer
|
||||
#define fscrypt_fname_free_buffer fscrypt_notsupp_fname_free_buffer
|
||||
#define fscrypt_fname_disk_to_usr fscrypt_notsupp_fname_disk_to_usr
|
||||
#define fscrypt_fname_usr_to_disk fscrypt_notsupp_fname_usr_to_disk
|
||||
#endif
|
||||
|
||||
/* dir.c */
|
||||
|
|
@ -2373,17 +2339,16 @@ extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
|||
void *buf, int buf_size,
|
||||
struct ext4_filename *fname,
|
||||
struct ext4_dir_entry_2 **dest_de);
|
||||
int ext4_insert_dentry(struct inode *dir,
|
||||
struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int buf_size,
|
||||
struct ext4_filename *fname);
|
||||
void ext4_insert_dentry(struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int buf_size,
|
||||
struct ext4_filename *fname);
|
||||
static inline void ext4_update_dx_flag(struct inode *inode)
|
||||
{
|
||||
if (!ext4_has_feature_dir_index(inode->i_sb))
|
||||
ext4_clear_inode_flag(inode, EXT4_INODE_INDEX);
|
||||
}
|
||||
static unsigned char ext4_filetype_table[] = {
|
||||
static const unsigned char ext4_filetype_table[] = {
|
||||
DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
|
||||
};
|
||||
|
||||
|
|
@ -3070,7 +3035,7 @@ extern int ext4_handle_dirty_dirent_node(handle_t *handle,
|
|||
struct inode *inode,
|
||||
struct buffer_head *bh);
|
||||
#define S_SHIFT 12
|
||||
static unsigned char ext4_type_by_mode[S_IFMT >> S_SHIFT] = {
|
||||
static const unsigned char ext4_type_by_mode[S_IFMT >> S_SHIFT] = {
|
||||
[S_IFREG >> S_SHIFT] = EXT4_FT_REG_FILE,
|
||||
[S_IFDIR >> S_SHIFT] = EXT4_FT_DIR,
|
||||
[S_IFCHR >> S_SHIFT] = EXT4_FT_CHRDEV,
|
||||
|
|
|
|||
|
|
@ -771,7 +771,7 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
|
|||
if (err)
|
||||
return ERR_PTR(err);
|
||||
if (!fscrypt_has_encryption_key(dir))
|
||||
return ERR_PTR(-EPERM);
|
||||
return ERR_PTR(-ENOKEY);
|
||||
if (!handle)
|
||||
nblocks += EXT4_DATA_TRANS_BLOCKS(dir->i_sb);
|
||||
encrypt = 1;
|
||||
|
|
@ -1093,6 +1093,17 @@ got:
|
|||
if (err)
|
||||
goto fail_drop;
|
||||
|
||||
/*
|
||||
* Since the encryption xattr will always be unique, create it first so
|
||||
* that it's less likely to end up in an external xattr block and
|
||||
* prevent its deduplication.
|
||||
*/
|
||||
if (encrypt) {
|
||||
err = fscrypt_inherit_context(dir, inode, handle, true);
|
||||
if (err)
|
||||
goto fail_free_drop;
|
||||
}
|
||||
|
||||
err = ext4_init_acl(handle, inode, dir);
|
||||
if (err)
|
||||
goto fail_free_drop;
|
||||
|
|
@ -1114,13 +1125,6 @@ got:
|
|||
ei->i_datasync_tid = handle->h_transaction->t_tid;
|
||||
}
|
||||
|
||||
if (encrypt) {
|
||||
/* give pointer to avoid set_context with journal ops. */
|
||||
err = fscrypt_inherit_context(dir, inode, &encrypt, true);
|
||||
if (err)
|
||||
goto fail_free_drop;
|
||||
}
|
||||
|
||||
err = ext4_mark_inode_dirty(handle, inode);
|
||||
if (err) {
|
||||
ext4_std_error(sb, err);
|
||||
|
|
|
|||
|
|
@ -1021,7 +1021,7 @@ 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(dir, inode, de, inline_size, fname);
|
||||
ext4_insert_dentry(inode, de, inline_size, fname);
|
||||
|
||||
ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size);
|
||||
|
||||
|
|
|
|||
|
|
@ -1165,7 +1165,8 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
|
|||
if (unlikely(err))
|
||||
page_zero_new_buffers(page, from, to);
|
||||
else if (decrypt)
|
||||
err = fscrypt_decrypt_page(page);
|
||||
err = fscrypt_decrypt_page(page->mapping->host, page,
|
||||
PAGE_SIZE, 0, page->index);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -3755,7 +3756,8 @@ static int __ext4_block_zero_page_range(handle_t *handle,
|
|||
/* We expect the key to be set. */
|
||||
BUG_ON(!fscrypt_has_encryption_key(inode));
|
||||
BUG_ON(blocksize != PAGE_SIZE);
|
||||
WARN_ON_ONCE(fscrypt_decrypt_page(page));
|
||||
WARN_ON_ONCE(fscrypt_decrypt_page(page->mapping->host,
|
||||
page, PAGE_SIZE, 0, page->index));
|
||||
}
|
||||
}
|
||||
if (ext4_should_journal_data(inode)) {
|
||||
|
|
|
|||
|
|
@ -191,6 +191,7 @@ journal_err_out:
|
|||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
static int uuid_is_zero(__u8 u[16])
|
||||
{
|
||||
int i;
|
||||
|
|
@ -200,6 +201,7 @@ static int uuid_is_zero(__u8 u[16])
|
|||
return 0;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ext4_ioctl_setflags(struct inode *inode,
|
||||
unsigned int flags)
|
||||
|
|
@ -765,28 +767,19 @@ resizefs_out:
|
|||
}
|
||||
case EXT4_IOC_PRECACHE_EXTENTS:
|
||||
return ext4_ext_precache(inode);
|
||||
case EXT4_IOC_SET_ENCRYPTION_POLICY: {
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
struct fscrypt_policy policy;
|
||||
|
||||
case EXT4_IOC_SET_ENCRYPTION_POLICY:
|
||||
if (!ext4_has_feature_encrypt(sb))
|
||||
return -EOPNOTSUPP;
|
||||
return fscrypt_ioctl_set_policy(filp, (const void __user *)arg);
|
||||
|
||||
if (copy_from_user(&policy,
|
||||
(struct fscrypt_policy __user *)arg,
|
||||
sizeof(policy)))
|
||||
return -EFAULT;
|
||||
return fscrypt_process_policy(filp, &policy);
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
case EXT4_IOC_GET_ENCRYPTION_PWSALT: {
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
int err, err2;
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
handle_t *handle;
|
||||
|
||||
if (!ext4_sb_has_crypto(sb))
|
||||
if (!ext4_has_feature_encrypt(sb))
|
||||
return -EOPNOTSUPP;
|
||||
if (uuid_is_zero(sbi->s_es->s_encrypt_pw_salt)) {
|
||||
err = mnt_want_write_file(filp);
|
||||
|
|
@ -816,30 +809,18 @@ resizefs_out:
|
|||
sbi->s_es->s_encrypt_pw_salt, 16))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case EXT4_IOC_GET_ENCRYPTION_POLICY: {
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
struct fscrypt_policy policy;
|
||||
int err = 0;
|
||||
|
||||
if (!ext4_encrypted_inode(inode))
|
||||
return -ENOENT;
|
||||
err = fscrypt_get_policy(inode, &policy);
|
||||
if (err)
|
||||
return err;
|
||||
if (copy_to_user((void __user *)arg, &policy, sizeof(policy)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
case EXT4_IOC_GET_ENCRYPTION_POLICY:
|
||||
return fscrypt_ioctl_get_policy(filp, (void __user *)arg);
|
||||
|
||||
case EXT4_IOC_FSGETXATTR:
|
||||
{
|
||||
struct fsxattr fa;
|
||||
|
||||
memset(&fa, 0, sizeof(struct fsxattr));
|
||||
ext4_get_inode_flags(ei);
|
||||
fa.fsx_xflags = ext4_iflags_to_xflags(ei->i_flags & EXT4_FL_USER_VISIBLE);
|
||||
|
||||
if (ext4_has_feature_project(inode->i_sb)) {
|
||||
|
|
|
|||
145
fs/ext4/namei.c
145
fs/ext4/namei.c
|
|
@ -1237,37 +1237,24 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
|
|||
}
|
||||
|
||||
/*
|
||||
* NOTE! unlike strncmp, ext4_match returns 1 for success, 0 for failure.
|
||||
* Test whether a directory entry matches the filename being searched for.
|
||||
*
|
||||
* `len <= EXT4_NAME_LEN' is guaranteed by caller.
|
||||
* `de != NULL' is guaranteed by caller.
|
||||
* Return: %true if the directory entry matches, otherwise %false.
|
||||
*/
|
||||
static inline int ext4_match(struct ext4_filename *fname,
|
||||
struct ext4_dir_entry_2 *de)
|
||||
static inline bool ext4_match(const struct ext4_filename *fname,
|
||||
const struct ext4_dir_entry_2 *de)
|
||||
{
|
||||
const void *name = fname_name(fname);
|
||||
u32 len = fname_len(fname);
|
||||
struct fscrypt_name f;
|
||||
|
||||
if (!de->inode)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
f.usr_fname = fname->usr_fname;
|
||||
f.disk_name = fname->disk_name;
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
if (unlikely(!name)) {
|
||||
if (fname->usr_fname->name[0] == '_') {
|
||||
int ret;
|
||||
if (de->name_len <= 32)
|
||||
return 0;
|
||||
ret = memcmp(de->name + ((de->name_len - 17) & ~15),
|
||||
fname->crypto_buf.name + 8, 16);
|
||||
return (ret == 0) ? 1 : 0;
|
||||
}
|
||||
name = fname->crypto_buf.name;
|
||||
len = fname->crypto_buf.len;
|
||||
}
|
||||
f.crypto_buf = fname->crypto_buf;
|
||||
#endif
|
||||
if (de->name_len != len)
|
||||
return 0;
|
||||
return (memcmp(de->name, name, len) == 0) ? 1 : 0;
|
||||
return fscrypt_match_name(&f, de->name, de->name_len);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1281,48 +1268,31 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
|
|||
struct ext4_dir_entry_2 * de;
|
||||
char * dlimit;
|
||||
int de_len;
|
||||
int res;
|
||||
|
||||
de = (struct ext4_dir_entry_2 *)search_buf;
|
||||
dlimit = search_buf + buf_size;
|
||||
while ((char *) de < dlimit) {
|
||||
/* this code is executed quadratically often */
|
||||
/* do minimal checking `by hand' */
|
||||
if ((char *) de + de->name_len <= dlimit) {
|
||||
res = ext4_match(fname, de);
|
||||
if (res < 0) {
|
||||
res = -1;
|
||||
goto return_result;
|
||||
}
|
||||
if (res > 0) {
|
||||
/* found a match - just to be sure, do
|
||||
* a full check */
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh,
|
||||
bh->b_data,
|
||||
bh->b_size, offset)) {
|
||||
res = -1;
|
||||
goto return_result;
|
||||
}
|
||||
*res_dir = de;
|
||||
res = 1;
|
||||
goto return_result;
|
||||
}
|
||||
|
||||
if ((char *) de + de->name_len <= dlimit &&
|
||||
ext4_match(fname, de)) {
|
||||
/* found a match - just to be sure, do
|
||||
* a full check */
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
|
||||
bh->b_size, offset))
|
||||
return -1;
|
||||
*res_dir = de;
|
||||
return 1;
|
||||
}
|
||||
/* prevent looping on a bad block */
|
||||
de_len = ext4_rec_len_from_disk(de->rec_len,
|
||||
dir->i_sb->s_blocksize);
|
||||
if (de_len <= 0) {
|
||||
res = -1;
|
||||
goto return_result;
|
||||
}
|
||||
if (de_len <= 0)
|
||||
return -1;
|
||||
offset += de_len;
|
||||
de = (struct ext4_dir_entry_2 *) ((char *) de + de_len);
|
||||
}
|
||||
|
||||
res = 0;
|
||||
return_result:
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block,
|
||||
|
|
@ -1378,6 +1348,8 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
|
|||
return NULL;
|
||||
|
||||
retval = ext4_fname_setup_filename(dir, d_name, 1, &fname);
|
||||
if (retval == -ENOENT)
|
||||
return NULL;
|
||||
if (retval)
|
||||
return ERR_PTR(retval);
|
||||
|
||||
|
|
@ -1614,16 +1586,9 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
|
|||
if (!IS_ERR(inode) && ext4_encrypted_inode(dir) &&
|
||||
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
||||
!fscrypt_has_permitted_context(dir, inode)) {
|
||||
int nokey = ext4_encrypted_inode(inode) &&
|
||||
!fscrypt_has_encryption_key(inode);
|
||||
if (nokey) {
|
||||
iput(inode);
|
||||
return ERR_PTR(-ENOKEY);
|
||||
}
|
||||
ext4_warning(inode->i_sb,
|
||||
"Inconsistent encryption contexts: %lu/%lu",
|
||||
(unsigned long) dir->i_ino,
|
||||
(unsigned long) inode->i_ino);
|
||||
dir->i_ino, inode->i_ino);
|
||||
iput(inode);
|
||||
return ERR_PTR(-EPERM);
|
||||
}
|
||||
|
|
@ -1831,24 +1796,15 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
|||
int nlen, rlen;
|
||||
unsigned int offset = 0;
|
||||
char *top;
|
||||
int res;
|
||||
|
||||
de = (struct ext4_dir_entry_2 *)buf;
|
||||
top = buf + buf_size - reclen;
|
||||
while ((char *) de <= top) {
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh,
|
||||
buf, buf_size, offset)) {
|
||||
res = -EFSCORRUPTED;
|
||||
goto return_result;
|
||||
}
|
||||
/* Provide crypto context and crypto buffer to ext4 match */
|
||||
res = ext4_match(fname, de);
|
||||
if (res < 0)
|
||||
goto return_result;
|
||||
if (res > 0) {
|
||||
res = -EEXIST;
|
||||
goto return_result;
|
||||
}
|
||||
buf, buf_size, offset))
|
||||
return -EFSCORRUPTED;
|
||||
if (ext4_match(fname, de))
|
||||
return -EEXIST;
|
||||
nlen = EXT4_DIR_REC_LEN(de->name_len);
|
||||
rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
|
||||
if ((de->inode ? rlen - nlen : rlen) >= reclen)
|
||||
|
|
@ -1856,22 +1812,17 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
|||
de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
|
||||
offset += rlen;
|
||||
}
|
||||
|
||||
if ((char *) de > top)
|
||||
res = -ENOSPC;
|
||||
else {
|
||||
*dest_de = de;
|
||||
res = 0;
|
||||
}
|
||||
return_result:
|
||||
return res;
|
||||
return -ENOSPC;
|
||||
|
||||
*dest_de = de;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ext4_insert_dentry(struct inode *dir,
|
||||
struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int buf_size,
|
||||
struct ext4_filename *fname)
|
||||
void ext4_insert_dentry(struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int buf_size,
|
||||
struct ext4_filename *fname)
|
||||
{
|
||||
|
||||
int nlen, rlen;
|
||||
|
|
@ -1890,7 +1841,6 @@ int ext4_insert_dentry(struct inode *dir,
|
|||
ext4_set_de_type(inode->i_sb, de, inode->i_mode);
|
||||
de->name_len = fname_len(fname);
|
||||
memcpy(de->name, fname_name(fname), fname_len(fname));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1926,11 +1876,8 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
|
|||
return err;
|
||||
}
|
||||
|
||||
/* By now the buffer is marked for journaling. Due to crypto operations,
|
||||
* the following function call may fail */
|
||||
err = ext4_insert_dentry(dir, inode, de, blocksize, fname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* By now the buffer is marked for journaling */
|
||||
ext4_insert_dentry(inode, de, blocksize, fname);
|
||||
|
||||
/*
|
||||
* XXX shouldn't update any times until successful
|
||||
|
|
@ -3090,7 +3037,7 @@ static int ext4_symlink(struct inode *dir,
|
|||
if (err)
|
||||
return err;
|
||||
if (!fscrypt_has_encryption_key(dir))
|
||||
return -EPERM;
|
||||
return -ENOKEY;
|
||||
disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
|
||||
sizeof(struct fscrypt_symlink_data));
|
||||
sd = kzalloc(disk_link.len, GFP_KERNEL);
|
||||
|
|
@ -3527,6 +3474,12 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
EXT4_I(old_dentry->d_inode)->i_projid)))
|
||||
return -EXDEV;
|
||||
|
||||
if ((ext4_encrypted_inode(old_dir) &&
|
||||
!fscrypt_has_encryption_key(old_dir)) ||
|
||||
(ext4_encrypted_inode(new_dir) &&
|
||||
!fscrypt_has_encryption_key(new_dir)))
|
||||
return -ENOKEY;
|
||||
|
||||
retval = dquot_initialize(old.dir);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
|
@ -3726,6 +3679,12 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
u8 new_file_type;
|
||||
int retval;
|
||||
|
||||
if ((ext4_encrypted_inode(old_dir) &&
|
||||
!fscrypt_has_encryption_key(old_dir)) ||
|
||||
(ext4_encrypted_inode(new_dir) &&
|
||||
!fscrypt_has_encryption_key(new_dir)))
|
||||
return -ENOKEY;
|
||||
|
||||
if ((ext4_encrypted_inode(old_dir) ||
|
||||
ext4_encrypted_inode(new_dir)) &&
|
||||
(old_dir != new_dir) &&
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/fscrypto.h>
|
||||
|
||||
#include "ext4_jbd2.h"
|
||||
#include "xattr.h"
|
||||
|
|
@ -470,7 +469,8 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
|
|||
gfp_t gfp_flags = GFP_NOFS;
|
||||
|
||||
retry_encrypt:
|
||||
data_page = fscrypt_encrypt_page(inode, page, gfp_flags);
|
||||
data_page = fscrypt_encrypt_page(inode, page, PAGE_SIZE, 0,
|
||||
page->index, gfp_flags);
|
||||
if (IS_ERR(data_page)) {
|
||||
ret = PTR_ERR(data_page);
|
||||
if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) {
|
||||
|
|
|
|||
|
|
@ -1102,51 +1102,65 @@ static int ext4_get_context(struct inode *inode, void *ctx, size_t len)
|
|||
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;
|
||||
handle_t *handle = fs_data;
|
||||
int res, res2, retries = 0;
|
||||
|
||||
/* 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);
|
||||
res = ext4_convert_inline_data(inode);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
/*
|
||||
* If a journal handle was specified, then the encryption context is
|
||||
* being set on a new inode via inheritance and is part of a larger
|
||||
* transaction to create the inode. Otherwise the encryption context is
|
||||
* being set on an existing inode in its own transaction. Only in the
|
||||
* latter case should the "retry on ENOSPC" logic be used.
|
||||
*/
|
||||
|
||||
if (handle) {
|
||||
res = ext4_xattr_set_handle(handle, 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);
|
||||
/*
|
||||
* Update inode->i_flags - e.g. S_DAX may get disabled
|
||||
*/
|
||||
ext4_set_inode_flags(inode);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
res = dquot_initialize(inode);
|
||||
if (res)
|
||||
return res;
|
||||
retry:
|
||||
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);
|
||||
res = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
ctx, len, 0);
|
||||
if (!res) {
|
||||
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
/* Update inode->i_flags - e.g. S_DAX may get disabled */
|
||||
ext4_set_inode_flags(inode);
|
||||
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 == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
|
||||
goto retry;
|
||||
if (!res)
|
||||
res = res2;
|
||||
return res;
|
||||
|
|
@ -1163,10 +1177,9 @@ static unsigned ext4_max_namelen(struct inode *inode)
|
|||
EXT4_NAME_LEN;
|
||||
}
|
||||
|
||||
static struct fscrypt_operations ext4_cryptops = {
|
||||
static const struct fscrypt_operations ext4_cryptops = {
|
||||
.key_prefix = "ext4:",
|
||||
.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,
|
||||
|
|
@ -1174,7 +1187,7 @@ static struct fscrypt_operations ext4_cryptops = {
|
|||
.max_namelen = ext4_max_namelen,
|
||||
};
|
||||
#else
|
||||
static struct fscrypt_operations ext4_cryptops = {
|
||||
static const struct fscrypt_operations ext4_cryptops = {
|
||||
.is_encrypted = ext4_encrypted_inode,
|
||||
};
|
||||
#endif
|
||||
|
|
@ -4199,11 +4212,6 @@ no_journal:
|
|||
ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10);
|
||||
|
||||
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;
|
||||
|
||||
cantfind_ext4:
|
||||
|
|
|
|||
|
|
@ -384,7 +384,7 @@ int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
f2fs_mark_inode_dirty_sync(inode);
|
||||
f2fs_mark_inode_dirty_sync(inode, true);
|
||||
|
||||
if (default_acl) {
|
||||
error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl,
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index,
|
|||
.sbi = sbi,
|
||||
.type = META,
|
||||
.op = REQ_OP_READ,
|
||||
.op_flags = READ_SYNC | REQ_META | REQ_PRIO,
|
||||
.op_flags = REQ_META | REQ_PRIO,
|
||||
.old_blkaddr = index,
|
||||
.new_blkaddr = index,
|
||||
.encrypted_page = NULL,
|
||||
|
|
@ -160,7 +160,7 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
|
|||
.sbi = sbi,
|
||||
.type = META,
|
||||
.op = REQ_OP_READ,
|
||||
.op_flags = sync ? (READ_SYNC | REQ_META | REQ_PRIO) : REQ_RAHEAD,
|
||||
.op_flags = sync ? (REQ_META | REQ_PRIO) : REQ_RAHEAD,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
struct blk_plug plug;
|
||||
|
|
@ -228,7 +228,7 @@ void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index)
|
|||
f2fs_put_page(page, 0);
|
||||
|
||||
if (readahead)
|
||||
ra_meta_pages(sbi, index, MAX_BIO_BLOCKS(sbi), META_POR, true);
|
||||
ra_meta_pages(sbi, index, BIO_MAX_PAGES, META_POR, true);
|
||||
}
|
||||
|
||||
static int f2fs_write_meta_page(struct page *page,
|
||||
|
|
@ -249,7 +249,8 @@ static int f2fs_write_meta_page(struct page *page,
|
|||
dec_page_count(sbi, F2FS_DIRTY_META);
|
||||
|
||||
if (wbc->for_reclaim)
|
||||
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, META, WRITE);
|
||||
f2fs_submit_merged_bio_cond(sbi, page->mapping->host,
|
||||
0, page->index, META, WRITE);
|
||||
|
||||
unlock_page(page);
|
||||
|
||||
|
|
@ -274,10 +275,11 @@ static int f2fs_write_meta_pages(struct address_space *mapping,
|
|||
get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META))
|
||||
goto skip_write;
|
||||
|
||||
trace_f2fs_writepages(mapping->host, wbc, META);
|
||||
/* if locked failed, cp will flush dirty pages instead */
|
||||
if (!mutex_trylock(&sbi->cp_mutex))
|
||||
goto skip_write;
|
||||
|
||||
/* if mounting is failed, skip writing node pages */
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
trace_f2fs_writepages(mapping->host, wbc, META);
|
||||
diff = nr_pages_to_write(sbi, META, wbc);
|
||||
written = sync_meta_pages(sbi, META, wbc->nr_to_write);
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
|
|
@ -493,6 +495,7 @@ int acquire_orphan_inode(struct f2fs_sb_info *sbi)
|
|||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
if (time_to_inject(sbi, FAULT_ORPHAN)) {
|
||||
spin_unlock(&im->ino_lock);
|
||||
f2fs_show_injection_info(FAULT_ORPHAN);
|
||||
return -ENOSPC;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -565,7 +568,7 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
|
|||
if (ni.blk_addr != NULL_ADDR) {
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"%s: orphan failed (ino=%x), run fsck to fix.",
|
||||
"%s: orphan failed (ino=%x) by kernel, retry mount.",
|
||||
__func__, ino);
|
||||
return -EIO;
|
||||
}
|
||||
|
|
@ -675,14 +678,13 @@ static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr,
|
|||
*cp_block = (struct f2fs_checkpoint *)page_address(*cp_page);
|
||||
|
||||
crc_offset = le32_to_cpu((*cp_block)->checksum_offset);
|
||||
if (crc_offset >= blk_size) {
|
||||
if (crc_offset > (blk_size - sizeof(__le32))) {
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"invalid crc_offset: %zu", crc_offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
crc = le32_to_cpu(*((__le32 *)((unsigned char *)*cp_block
|
||||
+ crc_offset)));
|
||||
crc = cur_cp_crc(*cp_block);
|
||||
if (!f2fs_crc_valid(sbi, crc, *cp_block, crc_offset)) {
|
||||
f2fs_msg(sbi->sb, KERN_WARNING, "invalid crc value");
|
||||
return -EINVAL;
|
||||
|
|
@ -770,7 +772,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi)
|
|||
|
||||
/* Sanity checking of checkpoint */
|
||||
if (sanity_check_ckpt(sbi))
|
||||
goto fail_no_cp;
|
||||
goto free_fail_no_cp;
|
||||
|
||||
if (cur_page == cp1)
|
||||
sbi->cur_cp_pack = 1;
|
||||
|
|
@ -798,6 +800,9 @@ done:
|
|||
f2fs_put_page(cp2, 1);
|
||||
return 0;
|
||||
|
||||
free_fail_no_cp:
|
||||
f2fs_put_page(cp1, 1);
|
||||
f2fs_put_page(cp2, 1);
|
||||
fail_no_cp:
|
||||
kfree(sbi->ckpt);
|
||||
return -EINVAL;
|
||||
|
|
@ -812,7 +817,9 @@ static void __add_dirty_inode(struct inode *inode, enum inode_type type)
|
|||
return;
|
||||
|
||||
set_inode_flag(inode, flag);
|
||||
list_add_tail(&F2FS_I(inode)->dirty_list, &sbi->inode_list[type]);
|
||||
if (!f2fs_is_volatile_file(inode))
|
||||
list_add_tail(&F2FS_I(inode)->dirty_list,
|
||||
&sbi->inode_list[type]);
|
||||
stat_inc_dirty_inode(sbi, type);
|
||||
}
|
||||
|
||||
|
|
@ -888,7 +895,7 @@ retry:
|
|||
F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA));
|
||||
return 0;
|
||||
}
|
||||
fi = list_entry(head->next, struct f2fs_inode_info, dirty_list);
|
||||
fi = list_first_entry(head, struct f2fs_inode_info, dirty_list);
|
||||
inode = igrab(&fi->vfs_inode);
|
||||
spin_unlock(&sbi->inode_lock[type]);
|
||||
if (inode) {
|
||||
|
|
@ -921,18 +928,35 @@ int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi)
|
|||
spin_unlock(&sbi->inode_lock[DIRTY_META]);
|
||||
return 0;
|
||||
}
|
||||
fi = list_entry(head->next, struct f2fs_inode_info,
|
||||
fi = list_first_entry(head, struct f2fs_inode_info,
|
||||
gdirty_list);
|
||||
inode = igrab(&fi->vfs_inode);
|
||||
spin_unlock(&sbi->inode_lock[DIRTY_META]);
|
||||
if (inode) {
|
||||
update_inode_page(inode);
|
||||
sync_inode_metadata(inode, 0);
|
||||
|
||||
/* it's on eviction */
|
||||
if (is_inode_flag_set(inode, FI_DIRTY_INODE))
|
||||
update_inode_page(inode);
|
||||
iput(inode);
|
||||
}
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __prepare_cp_block(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
nid_t last_nid = nm_i->next_scan_nid;
|
||||
|
||||
next_free_nid(sbi, &last_nid);
|
||||
ckpt->valid_block_count = cpu_to_le64(valid_user_blocks(sbi));
|
||||
ckpt->valid_node_count = cpu_to_le32(valid_node_count(sbi));
|
||||
ckpt->valid_inode_count = cpu_to_le32(valid_inode_count(sbi));
|
||||
ckpt->next_free_nid = cpu_to_le32(last_nid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Freeze all the FS-operations for checkpoint.
|
||||
*/
|
||||
|
|
@ -956,21 +980,26 @@ retry_flush_dents:
|
|||
err = sync_dirty_inodes(sbi, DIR_INODE);
|
||||
if (err)
|
||||
goto out;
|
||||
goto retry_flush_dents;
|
||||
}
|
||||
|
||||
if (get_pages(sbi, F2FS_DIRTY_IMETA)) {
|
||||
f2fs_unlock_all(sbi);
|
||||
err = f2fs_sync_inode_meta(sbi);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
goto retry_flush_dents;
|
||||
}
|
||||
|
||||
/*
|
||||
* POR: we should ensure that there are no dirty node pages
|
||||
* until finishing nat/sit flush.
|
||||
* until finishing nat/sit flush. inode->i_blocks can be updated.
|
||||
*/
|
||||
down_write(&sbi->node_change);
|
||||
|
||||
if (get_pages(sbi, F2FS_DIRTY_IMETA)) {
|
||||
up_write(&sbi->node_change);
|
||||
f2fs_unlock_all(sbi);
|
||||
err = f2fs_sync_inode_meta(sbi);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
goto retry_flush_dents;
|
||||
}
|
||||
|
||||
retry_flush_nodes:
|
||||
down_write(&sbi->node_write);
|
||||
|
||||
|
|
@ -978,11 +1007,20 @@ retry_flush_nodes:
|
|||
up_write(&sbi->node_write);
|
||||
err = sync_node_pages(sbi, &wbc);
|
||||
if (err) {
|
||||
up_write(&sbi->node_change);
|
||||
f2fs_unlock_all(sbi);
|
||||
goto out;
|
||||
}
|
||||
cond_resched();
|
||||
goto retry_flush_nodes;
|
||||
}
|
||||
|
||||
/*
|
||||
* sbi->node_change is used only for AIO write_begin path which produces
|
||||
* dirty node blocks and some checkpoint values by block allocation.
|
||||
*/
|
||||
__prepare_cp_block(sbi);
|
||||
up_write(&sbi->node_change);
|
||||
out:
|
||||
blk_finish_plug(&plug);
|
||||
return err;
|
||||
|
|
@ -991,8 +1029,6 @@ out:
|
|||
static void unblock_operations(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
up_write(&sbi->node_write);
|
||||
|
||||
build_free_nids(sbi);
|
||||
f2fs_unlock_all(sbi);
|
||||
}
|
||||
|
||||
|
|
@ -1003,7 +1039,7 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi)
|
|||
for (;;) {
|
||||
prepare_to_wait(&sbi->cp_wait, &wait, TASK_UNINTERRUPTIBLE);
|
||||
|
||||
if (!atomic_read(&sbi->nr_wb_bios))
|
||||
if (!get_pages(sbi, F2FS_WB_CP_DATA))
|
||||
break;
|
||||
|
||||
io_schedule_timeout(5*HZ);
|
||||
|
|
@ -1018,12 +1054,20 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
|
||||
spin_lock(&sbi->cp_lock);
|
||||
|
||||
if (cpc->reason == CP_UMOUNT)
|
||||
if ((cpc->reason & CP_UMOUNT) &&
|
||||
le32_to_cpu(ckpt->cp_pack_total_block_count) >
|
||||
sbi->blocks_per_seg - NM_I(sbi)->nat_bits_blocks)
|
||||
disable_nat_bits(sbi, false);
|
||||
|
||||
if (cpc->reason & CP_TRIMMED)
|
||||
__set_ckpt_flags(ckpt, CP_TRIMMED_FLAG);
|
||||
|
||||
if (cpc->reason & CP_UMOUNT)
|
||||
__set_ckpt_flags(ckpt, CP_UMOUNT_FLAG);
|
||||
else
|
||||
__clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG);
|
||||
|
||||
if (cpc->reason == CP_FASTBOOT)
|
||||
if (cpc->reason & CP_FASTBOOT)
|
||||
__set_ckpt_flags(ckpt, CP_FASTBOOT_FLAG);
|
||||
else
|
||||
__clear_ckpt_flags(ckpt, CP_FASTBOOT_FLAG);
|
||||
|
|
@ -1047,7 +1091,6 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num;
|
||||
nid_t last_nid = nm_i->next_scan_nid;
|
||||
block_t start_blk;
|
||||
unsigned int data_sum_blocks, orphan_blocks;
|
||||
__u32 crc32 = 0;
|
||||
|
|
@ -1064,14 +1107,11 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
next_free_nid(sbi, &last_nid);
|
||||
|
||||
/*
|
||||
* modify checkpoint
|
||||
* version number is already updated
|
||||
*/
|
||||
ckpt->elapsed_time = cpu_to_le64(get_mtime(sbi));
|
||||
ckpt->valid_block_count = cpu_to_le64(valid_user_blocks(sbi));
|
||||
ckpt->free_segment_count = cpu_to_le32(free_segments(sbi));
|
||||
for (i = 0; i < NR_CURSEG_NODE_TYPE; i++) {
|
||||
ckpt->cur_node_segno[i] =
|
||||
|
|
@ -1090,10 +1130,6 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
curseg_alloc_type(sbi, i + CURSEG_HOT_DATA);
|
||||
}
|
||||
|
||||
ckpt->valid_node_count = cpu_to_le32(valid_node_count(sbi));
|
||||
ckpt->valid_inode_count = cpu_to_le32(valid_inode_count(sbi));
|
||||
ckpt->next_free_nid = cpu_to_le32(last_nid);
|
||||
|
||||
/* 2 cp + n data seg summary + orphan inode blocks */
|
||||
data_sum_blocks = npages_for_summary_flush(sbi, false);
|
||||
spin_lock(&sbi->cp_lock);
|
||||
|
|
@ -1130,6 +1166,27 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
|
||||
start_blk = __start_cp_next_addr(sbi);
|
||||
|
||||
/* write nat bits */
|
||||
if (enabled_nat_bits(sbi, cpc)) {
|
||||
__u64 cp_ver = cur_cp_version(ckpt);
|
||||
block_t blk;
|
||||
|
||||
cp_ver |= ((__u64)crc32 << 32);
|
||||
*(__le64 *)nm_i->nat_bits = cpu_to_le64(cp_ver);
|
||||
|
||||
blk = start_blk + sbi->blocks_per_seg - nm_i->nat_bits_blocks;
|
||||
for (i = 0; i < nm_i->nat_bits_blocks; i++)
|
||||
update_meta_page(sbi, nm_i->nat_bits +
|
||||
(i << F2FS_BLKSIZE_BITS), blk + i);
|
||||
|
||||
/* Flush all the NAT BITS pages */
|
||||
while (get_pages(sbi, F2FS_DIRTY_META)) {
|
||||
sync_meta_pages(sbi, META, LONG_MAX);
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* need to wait for end_io results */
|
||||
wait_on_all_pages_writeback(sbi);
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
|
|
@ -1189,7 +1246,6 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
|
||||
clear_prefree_segments(sbi, cpc);
|
||||
clear_sbi_flag(sbi, SBI_IS_DIRTY);
|
||||
clear_sbi_flag(sbi, SBI_NEED_CP);
|
||||
__set_cp_next_pack(sbi);
|
||||
|
|
@ -1219,8 +1275,8 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
mutex_lock(&sbi->cp_mutex);
|
||||
|
||||
if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) &&
|
||||
(cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC ||
|
||||
(cpc->reason == CP_DISCARD && !sbi->discard_blks)))
|
||||
((cpc->reason & CP_FASTBOOT) || (cpc->reason & CP_SYNC) ||
|
||||
((cpc->reason & CP_DISCARD) && !sbi->discard_blks)))
|
||||
goto out;
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
err = -EIO;
|
||||
|
|
@ -1242,15 +1298,20 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
f2fs_flush_merged_bios(sbi);
|
||||
|
||||
/* this is the case of multiple fstrims without any changes */
|
||||
if (cpc->reason == CP_DISCARD && !is_sbi_flag_set(sbi, SBI_IS_DIRTY)) {
|
||||
f2fs_bug_on(sbi, NM_I(sbi)->dirty_nat_cnt);
|
||||
f2fs_bug_on(sbi, SIT_I(sbi)->dirty_sentries);
|
||||
f2fs_bug_on(sbi, prefree_segments(sbi));
|
||||
flush_sit_entries(sbi, cpc);
|
||||
clear_prefree_segments(sbi, cpc);
|
||||
f2fs_wait_all_discard_bio(sbi);
|
||||
unblock_operations(sbi);
|
||||
goto out;
|
||||
if (cpc->reason & CP_DISCARD) {
|
||||
if (!exist_trim_candidates(sbi, cpc)) {
|
||||
unblock_operations(sbi);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (NM_I(sbi)->dirty_nat_cnt == 0 &&
|
||||
SIT_I(sbi)->dirty_sentries == 0 &&
|
||||
prefree_segments(sbi) == 0) {
|
||||
flush_sit_entries(sbi, cpc);
|
||||
clear_prefree_segments(sbi, cpc);
|
||||
unblock_operations(sbi);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1262,18 +1323,20 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
ckpt->checkpoint_ver = cpu_to_le64(++ckpt_ver);
|
||||
|
||||
/* write cached NAT/SIT entries to NAT/SIT area */
|
||||
flush_nat_entries(sbi);
|
||||
flush_nat_entries(sbi, cpc);
|
||||
flush_sit_entries(sbi, cpc);
|
||||
|
||||
/* unlock all the fs_lock[] in do_checkpoint() */
|
||||
err = do_checkpoint(sbi, cpc);
|
||||
|
||||
f2fs_wait_all_discard_bio(sbi);
|
||||
if (err)
|
||||
release_discard_addrs(sbi);
|
||||
else
|
||||
clear_prefree_segments(sbi, cpc);
|
||||
|
||||
unblock_operations(sbi);
|
||||
stat_inc_cp_count(sbi->stat_info);
|
||||
|
||||
if (cpc->reason == CP_RECOVERY)
|
||||
if (cpc->reason & CP_RECOVERY)
|
||||
f2fs_msg(sbi->sb, KERN_NOTICE,
|
||||
"checkpoint: version = %llx", ckpt_ver);
|
||||
|
||||
|
|
|
|||
589
fs/f2fs/data.c
589
fs/f2fs/data.c
File diff suppressed because it is too large
Load diff
|
|
@ -50,7 +50,27 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
si->ndirty_files = sbi->ndirty_inode[FILE_INODE];
|
||||
si->ndirty_all = sbi->ndirty_inode[DIRTY_META];
|
||||
si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES);
|
||||
si->wb_bios = atomic_read(&sbi->nr_wb_bios);
|
||||
si->aw_cnt = atomic_read(&sbi->aw_cnt);
|
||||
si->vw_cnt = atomic_read(&sbi->vw_cnt);
|
||||
si->max_aw_cnt = atomic_read(&sbi->max_aw_cnt);
|
||||
si->max_vw_cnt = atomic_read(&sbi->max_vw_cnt);
|
||||
si->nr_wb_cp_data = get_pages(sbi, F2FS_WB_CP_DATA);
|
||||
si->nr_wb_data = get_pages(sbi, F2FS_WB_DATA);
|
||||
if (SM_I(sbi) && SM_I(sbi)->fcc_info) {
|
||||
si->nr_flushed =
|
||||
atomic_read(&SM_I(sbi)->fcc_info->issued_flush);
|
||||
si->nr_flushing =
|
||||
atomic_read(&SM_I(sbi)->fcc_info->issing_flush);
|
||||
}
|
||||
if (SM_I(sbi) && SM_I(sbi)->dcc_info) {
|
||||
si->nr_discarded =
|
||||
atomic_read(&SM_I(sbi)->dcc_info->issued_discard);
|
||||
si->nr_discarding =
|
||||
atomic_read(&SM_I(sbi)->dcc_info->issing_discard);
|
||||
si->nr_discard_cmd =
|
||||
atomic_read(&SM_I(sbi)->dcc_info->discard_cmd_cnt);
|
||||
si->undiscard_blks = SM_I(sbi)->dcc_info->undiscard_blks;
|
||||
}
|
||||
si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg;
|
||||
si->rsvd_segs = reserved_segments(sbi);
|
||||
si->overp_segs = overprovision_segments(sbi);
|
||||
|
|
@ -61,6 +81,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
si->inline_xattr = atomic_read(&sbi->inline_xattr);
|
||||
si->inline_inode = atomic_read(&sbi->inline_inode);
|
||||
si->inline_dir = atomic_read(&sbi->inline_dir);
|
||||
si->append = sbi->im[APPEND_INO].ino_num;
|
||||
si->update = sbi->im[UPDATE_INO].ino_num;
|
||||
si->orphans = sbi->im[ORPHAN_INO].ino_num;
|
||||
si->utilization = utilization(sbi);
|
||||
|
||||
|
|
@ -74,7 +96,9 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
si->dirty_nats = NM_I(sbi)->dirty_nat_cnt;
|
||||
si->sits = MAIN_SEGS(sbi);
|
||||
si->dirty_sits = SIT_I(sbi)->dirty_sentries;
|
||||
si->fnids = NM_I(sbi)->fcnt;
|
||||
si->free_nids = NM_I(sbi)->nid_cnt[FREE_NID_LIST];
|
||||
si->avail_nids = NM_I(sbi)->available_nids;
|
||||
si->alloc_nids = NM_I(sbi)->nid_cnt[ALLOC_NID_LIST];
|
||||
si->bg_gc = sbi->bg_gc;
|
||||
si->util_free = (int)(free_user_blocks(sbi) >> sbi->log_blocks_per_seg)
|
||||
* 100 / (int)(sbi->user_block_count >> sbi->log_blocks_per_seg)
|
||||
|
|
@ -87,8 +111,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_NODE; i++) {
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, i);
|
||||
si->curseg[i] = curseg->segno;
|
||||
si->cursec[i] = curseg->segno / sbi->segs_per_sec;
|
||||
si->curzone[i] = si->cursec[i] / sbi->secs_per_zone;
|
||||
si->cursec[i] = GET_SEC_FROM_SEG(sbi, curseg->segno);
|
||||
si->curzone[i] = GET_ZONE_FROM_SEC(sbi, si->cursec[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
|
|
@ -112,10 +136,10 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
|
|||
|
||||
bimodal = 0;
|
||||
total_vblocks = 0;
|
||||
blks_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg;
|
||||
blks_per_sec = BLKS_PER_SEC(sbi);
|
||||
hblks_per_sec = blks_per_sec / 2;
|
||||
for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
|
||||
vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec);
|
||||
vblocks = get_valid_blocks(sbi, segno, true);
|
||||
dist = abs(vblocks - hblks_per_sec);
|
||||
bimodal += dist * dist;
|
||||
|
||||
|
|
@ -144,7 +168,11 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
|||
if (si->base_mem)
|
||||
goto get_cache;
|
||||
|
||||
si->base_mem = sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize;
|
||||
/* build stat */
|
||||
si->base_mem = sizeof(struct f2fs_stat_info);
|
||||
|
||||
/* build superblock */
|
||||
si->base_mem += sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize;
|
||||
si->base_mem += 2 * sizeof(struct f2fs_inode_info);
|
||||
si->base_mem += sizeof(*sbi->ckpt);
|
||||
si->base_mem += sizeof(struct percpu_counter) * NR_COUNT_TYPE;
|
||||
|
|
@ -181,6 +209,10 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
|||
/* build nm */
|
||||
si->base_mem += sizeof(struct f2fs_nm_info);
|
||||
si->base_mem += __bitmap_size(sbi, NAT_BITMAP);
|
||||
si->base_mem += (NM_I(sbi)->nat_bits_blocks << F2FS_BLKSIZE_BITS);
|
||||
si->base_mem += NM_I(sbi)->nat_blocks * NAT_ENTRY_BITMAP_SIZE;
|
||||
si->base_mem += NM_I(sbi)->nat_blocks / 8;
|
||||
si->base_mem += NM_I(sbi)->nat_blocks * sizeof(unsigned short);
|
||||
|
||||
get_cache:
|
||||
si->cache_mem = 0;
|
||||
|
|
@ -190,11 +222,18 @@ get_cache:
|
|||
si->cache_mem += sizeof(struct f2fs_gc_kthread);
|
||||
|
||||
/* build merge flush thread */
|
||||
if (SM_I(sbi)->cmd_control_info)
|
||||
if (SM_I(sbi)->fcc_info)
|
||||
si->cache_mem += sizeof(struct flush_cmd_control);
|
||||
if (SM_I(sbi)->dcc_info) {
|
||||
si->cache_mem += sizeof(struct discard_cmd_control);
|
||||
si->cache_mem += sizeof(struct discard_cmd) *
|
||||
atomic_read(&SM_I(sbi)->dcc_info->discard_cmd_cnt);
|
||||
}
|
||||
|
||||
/* free nids */
|
||||
si->cache_mem += NM_I(sbi)->fcnt * sizeof(struct free_nid);
|
||||
si->cache_mem += (NM_I(sbi)->nid_cnt[FREE_NID_LIST] +
|
||||
NM_I(sbi)->nid_cnt[ALLOC_NID_LIST]) *
|
||||
sizeof(struct free_nid);
|
||||
si->cache_mem += NM_I(sbi)->nat_cnt * sizeof(struct nat_entry);
|
||||
si->cache_mem += NM_I(sbi)->dirty_nat_cnt *
|
||||
sizeof(struct nat_entry_set);
|
||||
|
|
@ -250,8 +289,8 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
si->inline_inode);
|
||||
seq_printf(s, " - Inline_dentry Inode: %u\n",
|
||||
si->inline_dir);
|
||||
seq_printf(s, " - Orphan Inode: %u\n",
|
||||
si->orphans);
|
||||
seq_printf(s, " - Orphan/Append/Update Inode: %u, %u, %u\n",
|
||||
si->orphans, si->append, si->update);
|
||||
seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n",
|
||||
si->main_area_segs, si->main_area_sections,
|
||||
si->main_area_zones);
|
||||
|
|
@ -310,8 +349,16 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n",
|
||||
si->ext_tree, si->zombie_tree, si->ext_node);
|
||||
seq_puts(s, "\nBalancing F2FS Async:\n");
|
||||
seq_printf(s, " - inmem: %4d, wb_bios: %4d\n",
|
||||
si->inmem_pages, si->wb_bios);
|
||||
seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: (%4d %4d), "
|
||||
"Discard: (%4d %4d)) cmd: %4d undiscard:%4u\n",
|
||||
si->nr_wb_cp_data, si->nr_wb_data,
|
||||
si->nr_flushing, si->nr_flushed,
|
||||
si->nr_discarding, si->nr_discarded,
|
||||
si->nr_discard_cmd, si->undiscard_blks);
|
||||
seq_printf(s, " - inmem: %4d, atomic IO: %4d (Max. %4d), "
|
||||
"volatile IO: %4d (Max. %4d)\n",
|
||||
si->inmem_pages, si->aw_cnt, si->max_aw_cnt,
|
||||
si->vw_cnt, si->max_vw_cnt);
|
||||
seq_printf(s, " - nodes: %4d in %4d\n",
|
||||
si->ndirty_node, si->node_pages);
|
||||
seq_printf(s, " - dents: %4d in dirs:%4d (%4d)\n",
|
||||
|
|
@ -324,8 +371,8 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
si->ndirty_imeta);
|
||||
seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n",
|
||||
si->dirty_nats, si->nats, si->dirty_sits, si->sits);
|
||||
seq_printf(s, " - free_nids: %9d\n",
|
||||
si->fnids);
|
||||
seq_printf(s, " - free_nids: %9d/%9d\n - alloc_nids: %9d\n",
|
||||
si->free_nids, si->avail_nids, si->alloc_nids);
|
||||
seq_puts(s, "\nDistribution of User Blocks:");
|
||||
seq_puts(s, " [ valid | invalid | free ]\n");
|
||||
seq_puts(s, " [");
|
||||
|
|
@ -410,6 +457,11 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi)
|
|||
atomic_set(&sbi->inline_dir, 0);
|
||||
atomic_set(&sbi->inplace_count, 0);
|
||||
|
||||
atomic_set(&sbi->aw_cnt, 0);
|
||||
atomic_set(&sbi->vw_cnt, 0);
|
||||
atomic_set(&sbi->max_aw_cnt, 0);
|
||||
atomic_set(&sbi->max_vw_cnt, 0);
|
||||
|
||||
mutex_lock(&f2fs_stat_mutex);
|
||||
list_add_tail(&si->stat_list, &f2fs_stat_list);
|
||||
mutex_unlock(&f2fs_stat_mutex);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue