Merge tag 'for-f2fs-4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs
Pull f2fs updates from Jaegeuk Kim: "New Features: - uplift filesystem encryption into fs/crypto/ - give sysfs entries to control memroy consumption Enhancements: - aio performance by preallocating blocks in ->write_iter - use writepages lock for only WB_SYNC_ALL - avoid redundant inline_data conversion - enhance forground GC - use wait_for_stable_page as possible - speed up SEEK_DATA and fiiemap Bug Fixes: - corner case in terms of -ENOSPC for inline_data - hung task caused by long latency in shrinker - corruption between atomic write and f2fs_trace_pid - avoid garbage lengths in dentries - revoke atomicly written pages if an error occurs In addition, there are various minor bug fixes and clean-ups" * tag 'for-f2fs-4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (81 commits) f2fs: submit node page write bios when really required f2fs: add missing argument to f2fs_setxattr stub f2fs: fix to avoid unneeded unlock_new_inode f2fs: clean up opened code with f2fs_update_dentry f2fs: declare static functions f2fs: use cryptoapi crc32 functions f2fs: modify the readahead method in ra_node_page() f2fs crypto: sync ext4_lookup and ext4_file_open fs crypto: move per-file encryption from f2fs tree to fs/crypto f2fs: mutex can't be used by down_write_nest_lock() f2fs: recovery missing dot dentries in root directory f2fs: fix to avoid deadlock when merging inline data f2fs: introduce f2fs_flush_merged_bios for cleanup f2fs: introduce f2fs_update_data_blkaddr for cleanup f2fs crypto: fix incorrect positioning for GCing encrypted data page f2fs: fix incorrect upper bound when iterating inode mapping tree f2fs: avoid hungtask problem caused by losing wake_up f2fs: trace old block address for CoWed page f2fs: try to flush inode after merging inline data f2fs: show more info about superblock recovery ...
This commit is contained in:
commit
d407574e79
40 changed files with 3136 additions and 2373 deletions
|
|
@ -98,3 +98,17 @@ Date: October 2015
|
|||
Contact: "Chao Yu" <chao2.yu@samsung.com>
|
||||
Description:
|
||||
Controls the count of nid pages to be readaheaded.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/dirty_nats_ratio
|
||||
Date: January 2016
|
||||
Contact: "Chao Yu" <chao2.yu@samsung.com>
|
||||
Description:
|
||||
Controls dirty nat entries ratio threshold, if current
|
||||
ratio exceeds configured threshold, checkpoint will
|
||||
be triggered for flushing dirty nat entries.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/lifetime_write_kbytes
|
||||
Date: January 2016
|
||||
Contact: "Shuoran Liu" <liushuoran@huawei.com>
|
||||
Description:
|
||||
Shows total written kbytes issued to disk.
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ config MANDATORY_FILE_LOCKING
|
|||
|
||||
To the best of my knowledge this is dead code that no one cares about.
|
||||
|
||||
source "fs/crypto/Kconfig"
|
||||
|
||||
source "fs/notify/Kconfig"
|
||||
|
||||
source "fs/quota/Kconfig"
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ obj-$(CONFIG_EVENTFD) += eventfd.o
|
|||
obj-$(CONFIG_USERFAULTFD) += userfaultfd.o
|
||||
obj-$(CONFIG_AIO) += aio.o
|
||||
obj-$(CONFIG_FS_DAX) += dax.o
|
||||
obj-$(CONFIG_FS_ENCRYPTION) += crypto/
|
||||
obj-$(CONFIG_FILE_LOCKING) += locks.o
|
||||
obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o
|
||||
obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o
|
||||
|
|
|
|||
18
fs/crypto/Kconfig
Normal file
18
fs/crypto/Kconfig
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
config FS_ENCRYPTION
|
||||
tristate "FS Encryption (Per-file encryption)"
|
||||
depends on BLOCK
|
||||
select CRYPTO
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ECB
|
||||
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
|
||||
efficient since it avoids caching the encrypted and
|
||||
decrypted pages in the page cache.
|
||||
3
fs/crypto/Makefile
Normal file
3
fs/crypto/Makefile
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o
|
||||
|
||||
fscrypto-y := crypto.o fname.o policy.o keyinfo.o
|
||||
555
fs/crypto/crypto.c
Normal file
555
fs/crypto/crypto.c
Normal file
|
|
@ -0,0 +1,555 @@
|
|||
/*
|
||||
* 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/mempool.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#include <linux/ecryptfs.h>
|
||||
|
||||
static unsigned int num_prealloc_crypto_pages = 32;
|
||||
static unsigned int num_prealloc_crypto_ctxs = 128;
|
||||
|
||||
module_param(num_prealloc_crypto_pages, uint, 0444);
|
||||
MODULE_PARM_DESC(num_prealloc_crypto_pages,
|
||||
"Number of crypto pages to preallocate");
|
||||
module_param(num_prealloc_crypto_ctxs, uint, 0444);
|
||||
MODULE_PARM_DESC(num_prealloc_crypto_ctxs,
|
||||
"Number of crypto contexts to preallocate");
|
||||
|
||||
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;
|
||||
static DEFINE_MUTEX(fscrypt_init_mutex);
|
||||
|
||||
static struct kmem_cache *fscrypt_ctx_cachep;
|
||||
struct kmem_cache *fscrypt_info_cachep;
|
||||
|
||||
/**
|
||||
* fscrypt_release_ctx() - Releases an encryption context
|
||||
* @ctx: The encryption context to release.
|
||||
*
|
||||
* If the encryption context was allocated from the pre-allocated pool, returns
|
||||
* it to that pool. Else, frees it.
|
||||
*
|
||||
* If there's a bounce page in the context, this frees that.
|
||||
*/
|
||||
void fscrypt_release_ctx(struct fscrypt_ctx *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (ctx->flags & FS_WRITE_PATH_FL && ctx->w.bounce_page) {
|
||||
mempool_free(ctx->w.bounce_page, fscrypt_bounce_page_pool);
|
||||
ctx->w.bounce_page = NULL;
|
||||
}
|
||||
ctx->w.control_page = NULL;
|
||||
if (ctx->flags & FS_CTX_REQUIRES_FREE_ENCRYPT_FL) {
|
||||
kmem_cache_free(fscrypt_ctx_cachep, ctx);
|
||||
} else {
|
||||
spin_lock_irqsave(&fscrypt_ctx_lock, flags);
|
||||
list_add(&ctx->free_list, &fscrypt_free_ctxs);
|
||||
spin_unlock_irqrestore(&fscrypt_ctx_lock, flags);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_release_ctx);
|
||||
|
||||
/**
|
||||
* fscrypt_get_ctx() - Gets an encryption context
|
||||
* @inode: The inode for which we are doing the crypto
|
||||
*
|
||||
* Allocates and initializes an encryption context.
|
||||
*
|
||||
* Return: An allocated and initialized encryption context on success; error
|
||||
* value or NULL otherwise.
|
||||
*/
|
||||
struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode)
|
||||
{
|
||||
struct fscrypt_ctx *ctx = NULL;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
unsigned long flags;
|
||||
|
||||
if (ci == NULL)
|
||||
return ERR_PTR(-ENOKEY);
|
||||
|
||||
/*
|
||||
* We first try getting the ctx from a free list because in
|
||||
* the common case the ctx will have an allocated and
|
||||
* initialized crypto tfm, so it's probably a worthwhile
|
||||
* optimization. For the bounce page, we first try getting it
|
||||
* from the kernel allocator because that's just about as fast
|
||||
* as getting it from a list and because a cache of free pages
|
||||
* should generally be a "last resort" option for a filesystem
|
||||
* to be able to do its job.
|
||||
*/
|
||||
spin_lock_irqsave(&fscrypt_ctx_lock, flags);
|
||||
ctx = list_first_entry_or_null(&fscrypt_free_ctxs,
|
||||
struct fscrypt_ctx, free_list);
|
||||
if (ctx)
|
||||
list_del(&ctx->free_list);
|
||||
spin_unlock_irqrestore(&fscrypt_ctx_lock, flags);
|
||||
if (!ctx) {
|
||||
ctx = kmem_cache_zalloc(fscrypt_ctx_cachep, GFP_NOFS);
|
||||
if (!ctx)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ctx->flags |= FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
|
||||
} else {
|
||||
ctx->flags &= ~FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
|
||||
}
|
||||
ctx->flags &= ~FS_WRITE_PATH_FL;
|
||||
return ctx;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_get_ctx);
|
||||
|
||||
/**
|
||||
* fscrypt_complete() - The completion callback for page encryption
|
||||
* @req: The asynchronous encryption request context
|
||||
* @res: The result of the encryption operation
|
||||
*/
|
||||
static void fscrypt_complete(struct crypto_async_request *req, int res)
|
||||
{
|
||||
struct fscrypt_completion_result *ecr = req->data;
|
||||
|
||||
if (res == -EINPROGRESS)
|
||||
return;
|
||||
ecr->res = 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)
|
||||
{
|
||||
u8 xts_tweak[FS_XTS_TWEAK_SIZE];
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_FS_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist dst, src;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
struct crypto_skcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
|
||||
req = skcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_request_alloc() failed\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
skcipher_request_set_callback(
|
||||
req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
fscrypt_complete, &ecr);
|
||||
|
||||
BUILD_BUG_ON(FS_XTS_TWEAK_SIZE < sizeof(index));
|
||||
memcpy(xts_tweak, &inode->i_ino, sizeof(index));
|
||||
memset(&xts_tweak[sizeof(index)], 0,
|
||||
FS_XTS_TWEAK_SIZE - sizeof(index));
|
||||
|
||||
sg_init_table(&dst, 1);
|
||||
sg_set_page(&dst, dest_page, PAGE_CACHE_SIZE, 0);
|
||||
sg_init_table(&src, 1);
|
||||
sg_set_page(&src, src_page, PAGE_CACHE_SIZE, 0);
|
||||
skcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE,
|
||||
xts_tweak);
|
||||
if (rw == FS_DECRYPT)
|
||||
res = crypto_skcipher_decrypt(req);
|
||||
else
|
||||
res = crypto_skcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
BUG_ON(req->base.data != &ecr);
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
skcipher_request_free(req);
|
||||
if (res) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_skcipher_encrypt() returned %d\n",
|
||||
__func__, res);
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx)
|
||||
{
|
||||
ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool,
|
||||
GFP_NOWAIT);
|
||||
if (ctx->w.bounce_page == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ctx->flags |= FS_WRITE_PATH_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.
|
||||
*
|
||||
* Allocates a ciphertext page and encrypts plaintext_page into it using the ctx
|
||||
* encryption context.
|
||||
*
|
||||
* Called on the page write path. 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
|
||||
* error value or NULL.
|
||||
*/
|
||||
struct page *fscrypt_encrypt_page(struct inode *inode,
|
||||
struct page *plaintext_page)
|
||||
{
|
||||
struct fscrypt_ctx *ctx;
|
||||
struct page *ciphertext_page = NULL;
|
||||
int err;
|
||||
|
||||
BUG_ON(!PageLocked(plaintext_page));
|
||||
|
||||
ctx = fscrypt_get_ctx(inode);
|
||||
if (IS_ERR(ctx))
|
||||
return (struct page *)ctx;
|
||||
|
||||
/* The encryption operation will require a bounce page. */
|
||||
ciphertext_page = alloc_bounce_page(ctx);
|
||||
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);
|
||||
if (err) {
|
||||
ciphertext_page = ERR_PTR(err);
|
||||
goto errout;
|
||||
}
|
||||
SetPagePrivate(ciphertext_page);
|
||||
set_page_private(ciphertext_page, (unsigned long)ctx);
|
||||
lock_page(ciphertext_page);
|
||||
return ciphertext_page;
|
||||
|
||||
errout:
|
||||
fscrypt_release_ctx(ctx);
|
||||
return ciphertext_page;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_encrypt_page);
|
||||
|
||||
/**
|
||||
* f2crypt_decrypt_page() - Decrypts a page in-place
|
||||
* @page: The page to decrypt. Must be locked.
|
||||
*
|
||||
* Decrypts page in-place using the ctx encryption context.
|
||||
*
|
||||
* Called from the read completion callback.
|
||||
*
|
||||
* Return: Zero on success, non-zero otherwise.
|
||||
*/
|
||||
int fscrypt_decrypt_page(struct page *page)
|
||||
{
|
||||
BUG_ON(!PageLocked(page));
|
||||
|
||||
return do_page_crypto(page->mapping->host,
|
||||
FS_DECRYPT, page->index, page, page);
|
||||
}
|
||||
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_CACHE_SIZE);
|
||||
|
||||
ctx = fscrypt_get_ctx(inode);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
|
||||
ciphertext_page = alloc_bounce_page(ctx);
|
||||
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);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
bio = bio_alloc(GFP_KERNEL, 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);
|
||||
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(WRITE, 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
|
||||
* removed.
|
||||
*/
|
||||
static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct inode *dir = d_inode(dentry->d_parent);
|
||||
struct fscrypt_info *ci = dir->i_crypt_info;
|
||||
int dir_has_key, cached_with_key;
|
||||
|
||||
if (!dir->i_sb->s_cop->is_encrypted(dir))
|
||||
return 0;
|
||||
|
||||
if (ci && ci->ci_keyring_key &&
|
||||
(ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
|
||||
(1 << KEY_FLAG_REVOKED) |
|
||||
(1 << KEY_FLAG_DEAD))))
|
||||
ci = NULL;
|
||||
|
||||
/* this should eventually be an flag in d_flags */
|
||||
spin_lock(&dentry->d_lock);
|
||||
cached_with_key = dentry->d_flags & DCACHE_ENCRYPTED_WITH_KEY;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
dir_has_key = (ci != NULL);
|
||||
|
||||
/*
|
||||
* If the dentry was cached without the key, and it is a
|
||||
* negative dentry, it might be a valid name. We can't check
|
||||
* if the key has since been made available due to locking
|
||||
* reasons, so we fail the validation so ext4_lookup() can do
|
||||
* this check.
|
||||
*
|
||||
* We also fail the validation if the dentry was created with
|
||||
* the key present, but we no longer have the key, or vice versa.
|
||||
*/
|
||||
if ((!cached_with_key && d_is_negative(dentry)) ||
|
||||
(!cached_with_key && dir_has_key) ||
|
||||
(cached_with_key && !dir_has_key))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct dentry_operations fscrypt_d_ops = {
|
||||
.d_revalidate = fscrypt_d_revalidate,
|
||||
};
|
||||
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;
|
||||
|
||||
ctx = (struct fscrypt_ctx *)page_private(page);
|
||||
set_page_private(page, (unsigned long)NULL);
|
||||
ClearPagePrivate(page);
|
||||
unlock_page(page);
|
||||
fscrypt_release_ctx(ctx);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_restore_control_page);
|
||||
|
||||
static void fscrypt_destroy(void)
|
||||
{
|
||||
struct fscrypt_ctx *pos, *n;
|
||||
|
||||
list_for_each_entry_safe(pos, n, &fscrypt_free_ctxs, free_list)
|
||||
kmem_cache_free(fscrypt_ctx_cachep, pos);
|
||||
INIT_LIST_HEAD(&fscrypt_free_ctxs);
|
||||
mempool_destroy(fscrypt_bounce_page_pool);
|
||||
fscrypt_bounce_page_pool = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fscrypt_initialize() - allocate major buffers for fs encryption.
|
||||
*
|
||||
* 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 i, res = -ENOMEM;
|
||||
|
||||
if (fscrypt_bounce_page_pool)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&fscrypt_init_mutex);
|
||||
if (fscrypt_bounce_page_pool)
|
||||
goto already_initialized;
|
||||
|
||||
for (i = 0; i < num_prealloc_crypto_ctxs; i++) {
|
||||
struct fscrypt_ctx *ctx;
|
||||
|
||||
ctx = kmem_cache_zalloc(fscrypt_ctx_cachep, GFP_NOFS);
|
||||
if (!ctx)
|
||||
goto fail;
|
||||
list_add(&ctx->free_list, &fscrypt_free_ctxs);
|
||||
}
|
||||
|
||||
fscrypt_bounce_page_pool =
|
||||
mempool_create_page_pool(num_prealloc_crypto_pages, 0);
|
||||
if (!fscrypt_bounce_page_pool)
|
||||
goto fail;
|
||||
|
||||
already_initialized:
|
||||
mutex_unlock(&fscrypt_init_mutex);
|
||||
return 0;
|
||||
fail:
|
||||
fscrypt_destroy();
|
||||
mutex_unlock(&fscrypt_init_mutex);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_initialize);
|
||||
|
||||
/**
|
||||
* fscrypt_init() - Set up for fs encryption.
|
||||
*/
|
||||
static int __init fscrypt_init(void)
|
||||
{
|
||||
fscrypt_read_workqueue = alloc_workqueue("fscrypt_read_queue",
|
||||
WQ_HIGHPRI, 0);
|
||||
if (!fscrypt_read_workqueue)
|
||||
goto fail;
|
||||
|
||||
fscrypt_ctx_cachep = KMEM_CACHE(fscrypt_ctx, SLAB_RECLAIM_ACCOUNT);
|
||||
if (!fscrypt_ctx_cachep)
|
||||
goto fail_free_queue;
|
||||
|
||||
fscrypt_info_cachep = KMEM_CACHE(fscrypt_info, SLAB_RECLAIM_ACCOUNT);
|
||||
if (!fscrypt_info_cachep)
|
||||
goto fail_free_ctx;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_free_ctx:
|
||||
kmem_cache_destroy(fscrypt_ctx_cachep);
|
||||
fail_free_queue:
|
||||
destroy_workqueue(fscrypt_read_workqueue);
|
||||
fail:
|
||||
return -ENOMEM;
|
||||
}
|
||||
module_init(fscrypt_init)
|
||||
|
||||
/**
|
||||
* fscrypt_exit() - Shutdown the fs encryption system
|
||||
*/
|
||||
static void __exit fscrypt_exit(void)
|
||||
{
|
||||
fscrypt_destroy();
|
||||
|
||||
if (fscrypt_read_workqueue)
|
||||
destroy_workqueue(fscrypt_read_workqueue);
|
||||
kmem_cache_destroy(fscrypt_ctx_cachep);
|
||||
kmem_cache_destroy(fscrypt_info_cachep);
|
||||
}
|
||||
module_exit(fscrypt_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
424
fs/crypto/fname.c
Normal file
424
fs/crypto/fname.c
Normal file
|
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
* This contains functions for filename crypto management
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
* Copyright (C) 2015, Motorola Mobility
|
||||
*
|
||||
* Written by Uday Savagaonkar, 2014.
|
||||
* Modified by Jaegeuk Kim, 2015.
|
||||
*
|
||||
* This has not yet undergone a rigorous security audit.
|
||||
*/
|
||||
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/fscrypto.h>
|
||||
|
||||
static u32 size_round_up(size_t size, size_t blksize)
|
||||
{
|
||||
return ((size + blksize - 1) / blksize) * blksize;
|
||||
}
|
||||
|
||||
/**
|
||||
* dir_crypt_complete() -
|
||||
*/
|
||||
static void dir_crypt_complete(struct crypto_async_request *req, int res)
|
||||
{
|
||||
struct fscrypt_completion_result *ecr = req->data;
|
||||
|
||||
if (res == -EINPROGRESS)
|
||||
return;
|
||||
ecr->res = res;
|
||||
complete(&ecr->completion);
|
||||
}
|
||||
|
||||
/**
|
||||
* fname_encrypt() -
|
||||
*
|
||||
* This function encrypts the input filename, and returns the length of the
|
||||
* ciphertext. Errors are returned as negative numbers. We trust the caller to
|
||||
* allocate sufficient memory to oname string.
|
||||
*/
|
||||
static int fname_encrypt(struct inode *inode,
|
||||
const struct qstr *iname, struct fscrypt_str *oname)
|
||||
{
|
||||
u32 ciphertext_len;
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_FS_COMPLETION_RESULT(ecr);
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
struct crypto_skcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
char iv[FS_CRYPTO_BLOCK_SIZE];
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
int padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK);
|
||||
char *workbuf, buf[32], *alloc_buf = NULL;
|
||||
unsigned lim;
|
||||
|
||||
lim = inode->i_sb->s_cop->max_namelen(inode);
|
||||
if (iname->len <= 0 || iname->len > lim)
|
||||
return -EIO;
|
||||
|
||||
ciphertext_len = (iname->len < FS_CRYPTO_BLOCK_SIZE) ?
|
||||
FS_CRYPTO_BLOCK_SIZE : iname->len;
|
||||
ciphertext_len = size_round_up(ciphertext_len, padding);
|
||||
ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len;
|
||||
|
||||
if (ciphertext_len <= sizeof(buf)) {
|
||||
workbuf = buf;
|
||||
} else {
|
||||
alloc_buf = kmalloc(ciphertext_len, GFP_NOFS);
|
||||
if (!alloc_buf)
|
||||
return -ENOMEM;
|
||||
workbuf = alloc_buf;
|
||||
}
|
||||
|
||||
/* Allocate request */
|
||||
req = skcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_request_alloc() failed\n", __func__);
|
||||
kfree(alloc_buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
skcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
dir_crypt_complete, &ecr);
|
||||
|
||||
/* Copy the input */
|
||||
memcpy(workbuf, iname->name, iname->len);
|
||||
if (iname->len < ciphertext_len)
|
||||
memset(workbuf + iname->len, 0, ciphertext_len - iname->len);
|
||||
|
||||
/* Initialize IV */
|
||||
memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
|
||||
|
||||
/* Create encryption request */
|
||||
sg_init_one(&src_sg, workbuf, ciphertext_len);
|
||||
sg_init_one(&dst_sg, oname->name, ciphertext_len);
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv);
|
||||
res = crypto_skcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
kfree(alloc_buf);
|
||||
skcipher_request_free(req);
|
||||
if (res < 0)
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: Error (error code %d)\n", __func__, res);
|
||||
|
||||
oname->len = ciphertext_len;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* fname_decrypt()
|
||||
* This function decrypts the input filename, and returns
|
||||
* the length of the plaintext.
|
||||
* Errors are returned as negative numbers.
|
||||
* We trust the caller to allocate sufficient memory to oname string.
|
||||
*/
|
||||
static int fname_decrypt(struct inode *inode,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_FS_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
struct crypto_skcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
char iv[FS_CRYPTO_BLOCK_SIZE];
|
||||
unsigned lim;
|
||||
|
||||
lim = inode->i_sb->s_cop->max_namelen(inode);
|
||||
if (iname->len <= 0 || iname->len > lim)
|
||||
return -EIO;
|
||||
|
||||
/* Allocate request */
|
||||
req = skcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_request_alloc() failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
skcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
dir_crypt_complete, &ecr);
|
||||
|
||||
/* Initialize IV */
|
||||
memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
|
||||
|
||||
/* Create decryption request */
|
||||
sg_init_one(&src_sg, iname->name, iname->len);
|
||||
sg_init_one(&dst_sg, oname->name, oname->len);
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv);
|
||||
res = crypto_skcipher_decrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
skcipher_request_free(req);
|
||||
if (res < 0) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: Error (error code %d)\n", __func__, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
oname->len = strnlen(oname->name, iname->len);
|
||||
return oname->len;
|
||||
}
|
||||
|
||||
static const char *lookup_table =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
||||
|
||||
/**
|
||||
* digest_encode() -
|
||||
*
|
||||
* Encodes the input digest using characters from the set [a-zA-Z0-9_+].
|
||||
* The encoded string is roughly 4/3 times the size of the input string.
|
||||
*/
|
||||
static int digest_encode(const char *src, int len, char *dst)
|
||||
{
|
||||
int i = 0, bits = 0, ac = 0;
|
||||
char *cp = dst;
|
||||
|
||||
while (i < len) {
|
||||
ac += (((unsigned char) src[i]) << bits);
|
||||
bits += 8;
|
||||
do {
|
||||
*cp++ = lookup_table[ac & 0x3f];
|
||||
ac >>= 6;
|
||||
bits -= 6;
|
||||
} while (bits >= 6);
|
||||
i++;
|
||||
}
|
||||
if (bits)
|
||||
*cp++ = lookup_table[ac & 0x3f];
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
static int digest_decode(const char *src, int len, char *dst)
|
||||
{
|
||||
int i = 0, bits = 0, ac = 0;
|
||||
const char *p;
|
||||
char *cp = dst;
|
||||
|
||||
while (i < len) {
|
||||
p = strchr(lookup_table, src[i]);
|
||||
if (p == NULL || src[i] == 0)
|
||||
return -2;
|
||||
ac += (p - lookup_table) << bits;
|
||||
bits += 6;
|
||||
if (bits >= 8) {
|
||||
*cp++ = ac & 0xff;
|
||||
ac >>= 8;
|
||||
bits -= 8;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (ac)
|
||||
return -1;
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
u32 fscrypt_fname_encrypted_size(struct inode *inode, u32 ilen)
|
||||
{
|
||||
int padding = 32;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
|
||||
if (ci)
|
||||
padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK);
|
||||
if (ilen < FS_CRYPTO_BLOCK_SIZE)
|
||||
ilen = FS_CRYPTO_BLOCK_SIZE;
|
||||
return size_round_up(ilen, padding);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_encrypted_size);
|
||||
|
||||
/**
|
||||
* fscrypt_fname_crypto_alloc_obuff() -
|
||||
*
|
||||
* 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,
|
||||
u32 ilen, struct fscrypt_str *crypto_str)
|
||||
{
|
||||
unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen);
|
||||
|
||||
crypto_str->len = olen;
|
||||
if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
|
||||
olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
|
||||
/*
|
||||
* Allocated buffer can hold one more character to null-terminate the
|
||||
* string
|
||||
*/
|
||||
crypto_str->name = kmalloc(olen + 1, GFP_NOFS);
|
||||
if (!(crypto_str->name))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_alloc_buffer);
|
||||
|
||||
/**
|
||||
* fscrypt_fname_crypto_free_buffer() -
|
||||
*
|
||||
* Frees the buffer allocated for crypto operation.
|
||||
*/
|
||||
void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str)
|
||||
{
|
||||
if (!crypto_str)
|
||||
return;
|
||||
kfree(crypto_str->name);
|
||||
crypto_str->name = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_free_buffer);
|
||||
|
||||
/**
|
||||
* fscrypt_fname_disk_to_usr() - converts a filename from disk space to user
|
||||
* space
|
||||
*/
|
||||
int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||
u32 hash, u32 minor_hash,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
const struct qstr qname = FSTR_TO_QSTR(iname);
|
||||
char buf[24];
|
||||
int ret;
|
||||
|
||||
if (fscrypt_is_dot_dotdot(&qname)) {
|
||||
oname->name[0] = '.';
|
||||
oname->name[iname->len - 1] = '.';
|
||||
oname->len = iname->len;
|
||||
return oname->len;
|
||||
}
|
||||
|
||||
if (iname->len < FS_CRYPTO_BLOCK_SIZE)
|
||||
return -EUCLEAN;
|
||||
|
||||
if (inode->i_crypt_info)
|
||||
return fname_decrypt(inode, iname, oname);
|
||||
|
||||
if (iname->len <= FS_FNAME_CRYPTO_DIGEST_SIZE) {
|
||||
ret = digest_encode(iname->name, iname->len, oname->name);
|
||||
oname->len = ret;
|
||||
return ret;
|
||||
}
|
||||
if (hash) {
|
||||
memcpy(buf, &hash, 4);
|
||||
memcpy(buf + 4, &minor_hash, 4);
|
||||
} else {
|
||||
memset(buf, 0, 8);
|
||||
}
|
||||
memcpy(buf + 8, iname->name + iname->len - 16, 16);
|
||||
oname->name[0] = '_';
|
||||
ret = digest_encode(buf, 24, oname->name + 1);
|
||||
oname->len = ret + 1;
|
||||
return ret + 1;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
|
||||
|
||||
/**
|
||||
* fscrypt_fname_usr_to_disk() - converts a filename from user space to disk
|
||||
* space
|
||||
*/
|
||||
int fscrypt_fname_usr_to_disk(struct inode *inode,
|
||||
const struct qstr *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
if (fscrypt_is_dot_dotdot(iname)) {
|
||||
oname->name[0] = '.';
|
||||
oname->name[iname->len - 1] = '.';
|
||||
oname->len = iname->len;
|
||||
return oname->len;
|
||||
}
|
||||
if (inode->i_crypt_info)
|
||||
return fname_encrypt(inode, iname, oname);
|
||||
/*
|
||||
* Without a proper key, a user is not allowed to modify the filenames
|
||||
* in a directory. Consequently, a user space name cannot be mapped to
|
||||
* a disk-space name
|
||||
*/
|
||||
return -EACCES;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_usr_to_disk);
|
||||
|
||||
int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct fscrypt_name *fname)
|
||||
{
|
||||
int ret = 0, bigname = 0;
|
||||
|
||||
memset(fname, 0, sizeof(struct fscrypt_name));
|
||||
fname->usr_fname = iname;
|
||||
|
||||
if (!dir->i_sb->s_cop->is_encrypted(dir) ||
|
||||
fscrypt_is_dot_dotdot(iname)) {
|
||||
fname->disk_name.name = (unsigned char *)iname->name;
|
||||
fname->disk_name.len = iname->len;
|
||||
return 0;
|
||||
}
|
||||
ret = get_crypt_info(dir);
|
||||
if (ret && ret != -EOPNOTSUPP)
|
||||
return ret;
|
||||
|
||||
if (dir->i_crypt_info) {
|
||||
ret = fscrypt_fname_alloc_buffer(dir, iname->len,
|
||||
&fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = fname_encrypt(dir, iname, &fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
goto errout;
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
return 0;
|
||||
}
|
||||
if (!lookup)
|
||||
return -EACCES;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
|
||||
if (fname->crypto_buf.name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = digest_decode(iname->name + bigname, iname->len - bigname,
|
||||
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);
|
||||
} else {
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
}
|
||||
return 0;
|
||||
|
||||
errout:
|
||||
fscrypt_fname_free_buffer(&fname->crypto_buf);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_setup_filename);
|
||||
|
||||
void fscrypt_free_filename(struct fscrypt_name *fname)
|
||||
{
|
||||
kfree(fname->crypto_buf.name);
|
||||
fname->crypto_buf.name = NULL;
|
||||
fname->usr_fname = NULL;
|
||||
fname->disk_name.name = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_free_filename);
|
||||
272
fs/crypto/keyinfo.c
Normal file
272
fs/crypto/keyinfo.c
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* key management facility for FS encryption support.
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains encryption key functions.
|
||||
*
|
||||
* Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
|
||||
*/
|
||||
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <uapi/linux/keyctl.h>
|
||||
#include <linux/fscrypto.h>
|
||||
|
||||
static void derive_crypt_complete(struct crypto_async_request *req, int rc)
|
||||
{
|
||||
struct fscrypt_completion_result *ecr = req->data;
|
||||
|
||||
if (rc == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
ecr->res = rc;
|
||||
complete(&ecr->completion);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE],
|
||||
u8 source_key[FS_AES_256_XTS_KEY_SIZE],
|
||||
u8 derived_key[FS_AES_256_XTS_KEY_SIZE])
|
||||
{
|
||||
int res = 0;
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_FS_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,
|
||||
FS_AES_128_ECB_KEY_SIZE);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
sg_init_one(&src_sg, source_key, FS_AES_256_XTS_KEY_SIZE);
|
||||
sg_init_one(&dst_sg, derived_key, FS_AES_256_XTS_KEY_SIZE);
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg,
|
||||
FS_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;
|
||||
}
|
||||
|
||||
static void put_crypt_info(struct fscrypt_info *ci)
|
||||
{
|
||||
if (!ci)
|
||||
return;
|
||||
|
||||
key_put(ci->ci_keyring_key);
|
||||
crypto_free_skcipher(ci->ci_ctfm);
|
||||
kmem_cache_free(fscrypt_info_cachep, ci);
|
||||
}
|
||||
|
||||
int get_crypt_info(struct inode *inode)
|
||||
{
|
||||
struct fscrypt_info *crypt_info;
|
||||
u8 full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE +
|
||||
(FS_KEY_DESCRIPTOR_SIZE * 2) + 1];
|
||||
struct key *keyring_key = NULL;
|
||||
struct fscrypt_key *master_key;
|
||||
struct fscrypt_context ctx;
|
||||
const struct user_key_payload *ukp;
|
||||
struct crypto_skcipher *ctfm;
|
||||
const char *cipher_str;
|
||||
u8 raw_key[FS_MAX_KEY_SIZE];
|
||||
u8 mode;
|
||||
int res;
|
||||
|
||||
res = fscrypt_initialize();
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (!inode->i_sb->s_cop->get_context)
|
||||
return -EOPNOTSUPP;
|
||||
retry:
|
||||
crypt_info = ACCESS_ONCE(inode->i_crypt_info);
|
||||
if (crypt_info) {
|
||||
if (!crypt_info->ci_keyring_key ||
|
||||
key_validate(crypt_info->ci_keyring_key) == 0)
|
||||
return 0;
|
||||
fscrypt_put_encryption_info(inode, crypt_info);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
if (res < 0) {
|
||||
if (!fscrypt_dummy_context_enabled(inode))
|
||||
return res;
|
||||
ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
|
||||
ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
|
||||
ctx.flags = 0;
|
||||
} else if (res != sizeof(ctx)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
res = 0;
|
||||
|
||||
crypt_info = kmem_cache_alloc(fscrypt_info_cachep, GFP_NOFS);
|
||||
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 FS_ENCRYPTION_MODE_AES_256_XTS:
|
||||
cipher_str = "xts(aes)";
|
||||
break;
|
||||
case FS_ENCRYPTION_MODE_AES_256_CTS:
|
||||
cipher_str = "cts(cbc(aes))";
|
||||
break;
|
||||
default:
|
||||
printk_once(KERN_WARNING
|
||||
"%s: unsupported key mode %d (ino %u)\n",
|
||||
__func__, mode, (unsigned) inode->i_ino);
|
||||
res = -ENOKEY;
|
||||
goto out;
|
||||
}
|
||||
if (fscrypt_dummy_context_enabled(inode)) {
|
||||
memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE);
|
||||
goto got_key;
|
||||
}
|
||||
memcpy(full_key_descriptor, FS_KEY_DESC_PREFIX,
|
||||
FS_KEY_DESC_PREFIX_SIZE);
|
||||
sprintf(full_key_descriptor + FS_KEY_DESC_PREFIX_SIZE,
|
||||
"%*phN", FS_KEY_DESCRIPTOR_SIZE,
|
||||
ctx.master_key_descriptor);
|
||||
full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE +
|
||||
(2 * FS_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
|
||||
"%s: key type must be logon\n", __func__);
|
||||
res = -ENOKEY;
|
||||
goto out;
|
||||
}
|
||||
down_read(&keyring_key->sem);
|
||||
ukp = user_key_payload(keyring_key);
|
||||
if (ukp->datalen != sizeof(struct fscrypt_key)) {
|
||||
res = -EINVAL;
|
||||
up_read(&keyring_key->sem);
|
||||
goto out;
|
||||
}
|
||||
master_key = (struct fscrypt_key *)ukp->data;
|
||||
BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE);
|
||||
|
||||
if (master_key->size != FS_AES_256_XTS_KEY_SIZE) {
|
||||
printk_once(KERN_WARNING
|
||||
"%s: key size incorrect: %d\n",
|
||||
__func__, master_key->size);
|
||||
res = -ENOKEY;
|
||||
up_read(&keyring_key->sem);
|
||||
goto out;
|
||||
}
|
||||
res = 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_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
|
||||
res = crypto_skcipher_setkey(ctfm, raw_key, fscrypt_key_size(mode));
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
memzero_explicit(raw_key, sizeof(raw_key));
|
||||
if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) != NULL) {
|
||||
put_crypt_info(crypt_info);
|
||||
goto retry;
|
||||
}
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (res == -ENOKEY)
|
||||
res = 0;
|
||||
put_crypt_info(crypt_info);
|
||||
memzero_explicit(raw_key, sizeof(raw_key));
|
||||
return res;
|
||||
}
|
||||
|
||||
void fscrypt_put_encryption_info(struct inode *inode, struct fscrypt_info *ci)
|
||||
{
|
||||
struct fscrypt_info *prev;
|
||||
|
||||
if (ci == NULL)
|
||||
ci = ACCESS_ONCE(inode->i_crypt_info);
|
||||
if (ci == NULL)
|
||||
return;
|
||||
|
||||
prev = cmpxchg(&inode->i_crypt_info, ci, NULL);
|
||||
if (prev != ci)
|
||||
return;
|
||||
|
||||
put_crypt_info(ci);
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_put_encryption_info);
|
||||
|
||||
int fscrypt_get_encryption_info(struct inode *inode)
|
||||
{
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
|
||||
if (!ci ||
|
||||
(ci->ci_keyring_key &&
|
||||
(ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
|
||||
(1 << KEY_FLAG_REVOKED) |
|
||||
(1 << KEY_FLAG_DEAD)))))
|
||||
return get_crypt_info(inode);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_get_encryption_info);
|
||||
229
fs/crypto/policy.c
Normal file
229
fs/crypto/policy.c
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Encryption policy functions for per-file encryption support.
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
* Copyright (C) 2015, Motorola Mobility.
|
||||
*
|
||||
* Written by Michael Halcrow, 2015.
|
||||
* Modified by Jaegeuk Kim, 2015.
|
||||
*/
|
||||
|
||||
#include <linux/random.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/fscrypto.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);
|
||||
}
|
||||
|
||||
/*
|
||||
* check whether the policy is consistent with the encryption context
|
||||
* for the inode
|
||||
*/
|
||||
static int is_encryption_context_consistent_with_policy(struct inode *inode,
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (policy->flags & ~FS_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) != FS_KEY_DERIVATION_NONCE_SIZE);
|
||||
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
|
||||
|
||||
return inode->i_sb->s_cop->set_context(inode, &ctx, sizeof(ctx), NULL);
|
||||
}
|
||||
|
||||
int fscrypt_process_policy(struct inode *inode,
|
||||
const struct fscrypt_policy *policy)
|
||||
{
|
||||
if (policy->version != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!inode_has_encryption_context(inode)) {
|
||||
if (!inode->i_sb->s_cop->empty_dir)
|
||||
return -EOPNOTSUPP;
|
||||
if (!inode->i_sb->s_cop->empty_dir(inode))
|
||||
return -ENOTEMPTY;
|
||||
return create_encryption_context_from_policy(inode, policy);
|
||||
}
|
||||
|
||||
if (is_encryption_context_consistent_with_policy(inode, policy))
|
||||
return 0;
|
||||
|
||||
printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_process_policy);
|
||||
|
||||
int fscrypt_get_policy(struct inode *inode, struct fscrypt_policy *policy)
|
||||
{
|
||||
struct fscrypt_context ctx;
|
||||
int res;
|
||||
|
||||
if (!inode->i_sb->s_cop->get_context ||
|
||||
!inode->i_sb->s_cop->is_encrypted(inode))
|
||||
return -ENODATA;
|
||||
|
||||
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
|
||||
if (res != sizeof(ctx))
|
||||
return -ENODATA;
|
||||
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,
|
||||
FS_KEY_DESCRIPTOR_SIZE);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_get_policy);
|
||||
|
||||
int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
|
||||
{
|
||||
struct fscrypt_info *parent_ci, *child_ci;
|
||||
int res;
|
||||
|
||||
if ((parent == NULL) || (child == NULL)) {
|
||||
printk(KERN_ERR "parent %p child %p\n", parent, child);
|
||||
BUG_ON(1);
|
||||
}
|
||||
|
||||
/* no restrictions if the parent directory is not encrypted */
|
||||
if (!parent->i_sb->s_cop->is_encrypted(parent))
|
||||
return 1;
|
||||
/* if the child directory is not encrypted, this is always a problem */
|
||||
if (!parent->i_sb->s_cop->is_encrypted(child))
|
||||
return 0;
|
||||
res = fscrypt_get_encryption_info(parent);
|
||||
if (res)
|
||||
return 0;
|
||||
res = fscrypt_get_encryption_info(child);
|
||||
if (res)
|
||||
return 0;
|
||||
parent_ci = parent->i_crypt_info;
|
||||
child_ci = 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,
|
||||
FS_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));
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_has_permitted_context);
|
||||
|
||||
/**
|
||||
* fscrypt_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.
|
||||
* @fs_data: private data given by FS.
|
||||
* @preload: preload child i_crypt_info
|
||||
*
|
||||
* Return: Zero on success, non-zero otherwise
|
||||
*/
|
||||
int fscrypt_inherit_context(struct inode *parent, struct inode *child,
|
||||
void *fs_data, bool preload)
|
||||
{
|
||||
struct fscrypt_context ctx;
|
||||
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;
|
||||
|
||||
ci = parent->i_crypt_info;
|
||||
if (ci == NULL)
|
||||
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);
|
||||
}
|
||||
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
|
||||
res = parent->i_sb->s_cop->set_context(child, &ctx,
|
||||
sizeof(ctx), fs_data);
|
||||
if (res)
|
||||
return res;
|
||||
return preload ? fscrypt_get_encryption_info(child): 0;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_inherit_context);
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
config F2FS_FS
|
||||
tristate "F2FS filesystem support"
|
||||
depends on BLOCK
|
||||
select CRYPTO
|
||||
select CRYPTO_CRC32
|
||||
help
|
||||
F2FS is based on Log-structured File System (LFS), which supports
|
||||
versatile "flash-friendly" features. The design has been focused on
|
||||
|
|
@ -76,15 +78,7 @@ config F2FS_FS_ENCRYPTION
|
|||
bool "F2FS Encryption"
|
||||
depends on F2FS_FS
|
||||
depends on F2FS_FS_XATTR
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_XTS
|
||||
select CRYPTO_CTS
|
||||
select CRYPTO_CTR
|
||||
select CRYPTO_SHA256
|
||||
select KEYS
|
||||
select ENCRYPTED_KEYS
|
||||
select FS_ENCRYPTION
|
||||
help
|
||||
Enable encryption of f2fs files and directories. This
|
||||
feature is similar to ecryptfs, but it is more memory
|
||||
|
|
|
|||
|
|
@ -7,5 +7,3 @@ f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
|
|||
f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
|
||||
f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
|
||||
f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o
|
||||
f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o crypto.o \
|
||||
crypto_key.o crypto_fname.o
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ repeat:
|
|||
cond_resched();
|
||||
goto repeat;
|
||||
}
|
||||
f2fs_wait_on_page_writeback(page, META);
|
||||
f2fs_wait_on_page_writeback(page, META, true);
|
||||
SetPageUptodate(page);
|
||||
return page;
|
||||
}
|
||||
|
|
@ -56,7 +56,8 @@ static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index,
|
|||
.sbi = sbi,
|
||||
.type = META,
|
||||
.rw = READ_SYNC | REQ_META | REQ_PRIO,
|
||||
.blk_addr = index,
|
||||
.old_blkaddr = index,
|
||||
.new_blkaddr = index,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
|
||||
|
|
@ -143,7 +144,6 @@ bool is_valid_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type)
|
|||
int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
|
||||
int type, bool sync)
|
||||
{
|
||||
block_t prev_blk_addr = 0;
|
||||
struct page *page;
|
||||
block_t blkno = start;
|
||||
struct f2fs_io_info fio = {
|
||||
|
|
@ -152,10 +152,12 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
|
|||
.rw = sync ? (READ_SYNC | REQ_META | REQ_PRIO) : READA,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
struct blk_plug plug;
|
||||
|
||||
if (unlikely(type == META_POR))
|
||||
fio.rw &= ~REQ_META;
|
||||
|
||||
blk_start_plug(&plug);
|
||||
for (; nrpages-- > 0; blkno++) {
|
||||
|
||||
if (!is_valid_blkaddr(sbi, blkno, type))
|
||||
|
|
@ -167,27 +169,24 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
|
|||
NAT_BLOCK_OFFSET(NM_I(sbi)->max_nid)))
|
||||
blkno = 0;
|
||||
/* get nat block addr */
|
||||
fio.blk_addr = current_nat_addr(sbi,
|
||||
fio.new_blkaddr = current_nat_addr(sbi,
|
||||
blkno * NAT_ENTRY_PER_BLOCK);
|
||||
break;
|
||||
case META_SIT:
|
||||
/* get sit block addr */
|
||||
fio.blk_addr = current_sit_addr(sbi,
|
||||
fio.new_blkaddr = current_sit_addr(sbi,
|
||||
blkno * SIT_ENTRY_PER_BLOCK);
|
||||
if (blkno != start && prev_blk_addr + 1 != fio.blk_addr)
|
||||
goto out;
|
||||
prev_blk_addr = fio.blk_addr;
|
||||
break;
|
||||
case META_SSA:
|
||||
case META_CP:
|
||||
case META_POR:
|
||||
fio.blk_addr = blkno;
|
||||
fio.new_blkaddr = blkno;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
page = grab_cache_page(META_MAPPING(sbi), fio.blk_addr);
|
||||
page = grab_cache_page(META_MAPPING(sbi), fio.new_blkaddr);
|
||||
if (!page)
|
||||
continue;
|
||||
if (PageUptodate(page)) {
|
||||
|
|
@ -196,11 +195,13 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
|
|||
}
|
||||
|
||||
fio.page = page;
|
||||
fio.old_blkaddr = fio.new_blkaddr;
|
||||
f2fs_submit_page_mbio(&fio);
|
||||
f2fs_put_page(page, 0);
|
||||
}
|
||||
out:
|
||||
f2fs_submit_merged_bio(sbi, META, READ);
|
||||
blk_finish_plug(&plug);
|
||||
return blkno - start;
|
||||
}
|
||||
|
||||
|
|
@ -232,13 +233,17 @@ static int f2fs_write_meta_page(struct page *page,
|
|||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
goto redirty_out;
|
||||
|
||||
f2fs_wait_on_page_writeback(page, META);
|
||||
write_meta_page(sbi, page);
|
||||
dec_page_count(sbi, F2FS_DIRTY_META);
|
||||
|
||||
if (wbc->for_reclaim)
|
||||
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, META, WRITE);
|
||||
|
||||
unlock_page(page);
|
||||
|
||||
if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi)))
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
f2fs_submit_merged_bio(sbi, META, WRITE);
|
||||
|
||||
return 0;
|
||||
|
||||
redirty_out:
|
||||
|
|
@ -252,13 +257,13 @@ static int f2fs_write_meta_pages(struct address_space *mapping,
|
|||
struct f2fs_sb_info *sbi = F2FS_M_SB(mapping);
|
||||
long diff, written;
|
||||
|
||||
trace_f2fs_writepages(mapping->host, wbc, META);
|
||||
|
||||
/* collect a number of dirty meta pages and write together */
|
||||
if (wbc->for_kupdate ||
|
||||
get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META))
|
||||
goto skip_write;
|
||||
|
||||
trace_f2fs_writepages(mapping->host, wbc, META);
|
||||
|
||||
/* if mounting is failed, skip writing node pages */
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
diff = nr_pages_to_write(sbi, META, wbc);
|
||||
|
|
@ -269,6 +274,7 @@ static int f2fs_write_meta_pages(struct address_space *mapping,
|
|||
|
||||
skip_write:
|
||||
wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_META);
|
||||
trace_f2fs_writepages(mapping->host, wbc, META);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -276,15 +282,18 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type,
|
|||
long nr_to_write)
|
||||
{
|
||||
struct address_space *mapping = META_MAPPING(sbi);
|
||||
pgoff_t index = 0, end = LONG_MAX, prev = LONG_MAX;
|
||||
pgoff_t index = 0, end = ULONG_MAX, prev = ULONG_MAX;
|
||||
struct pagevec pvec;
|
||||
long nwritten = 0;
|
||||
struct writeback_control wbc = {
|
||||
.for_reclaim = 0,
|
||||
};
|
||||
struct blk_plug plug;
|
||||
|
||||
pagevec_init(&pvec, 0);
|
||||
|
||||
blk_start_plug(&plug);
|
||||
|
||||
while (index <= end) {
|
||||
int i, nr_pages;
|
||||
nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
|
||||
|
|
@ -296,7 +305,7 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type,
|
|||
for (i = 0; i < nr_pages; i++) {
|
||||
struct page *page = pvec.pages[i];
|
||||
|
||||
if (prev == LONG_MAX)
|
||||
if (prev == ULONG_MAX)
|
||||
prev = page->index - 1;
|
||||
if (nr_to_write != LONG_MAX && page->index != prev + 1) {
|
||||
pagevec_release(&pvec);
|
||||
|
|
@ -315,6 +324,9 @@ continue_unlock:
|
|||
goto continue_unlock;
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(page, META, true);
|
||||
|
||||
BUG_ON(PageWriteback(page));
|
||||
if (!clear_page_dirty_for_io(page))
|
||||
goto continue_unlock;
|
||||
|
||||
|
|
@ -334,6 +346,8 @@ stop:
|
|||
if (nwritten)
|
||||
f2fs_submit_merged_bio(sbi, type, WRITE);
|
||||
|
||||
blk_finish_plug(&plug);
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
|
|
@ -621,7 +635,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
|
|||
goto invalid_cp1;
|
||||
|
||||
crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset)));
|
||||
if (!f2fs_crc_valid(crc, cp_block, crc_offset))
|
||||
if (!f2fs_crc_valid(sbi, crc, cp_block, crc_offset))
|
||||
goto invalid_cp1;
|
||||
|
||||
pre_version = cur_cp_version(cp_block);
|
||||
|
|
@ -636,7 +650,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
|
|||
goto invalid_cp2;
|
||||
|
||||
crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset)));
|
||||
if (!f2fs_crc_valid(crc, cp_block, crc_offset))
|
||||
if (!f2fs_crc_valid(sbi, crc, cp_block, crc_offset))
|
||||
goto invalid_cp2;
|
||||
|
||||
cur_version = cur_cp_version(cp_block);
|
||||
|
|
@ -696,6 +710,10 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi)
|
|||
cp_block = (struct f2fs_checkpoint *)page_address(cur_page);
|
||||
memcpy(sbi->ckpt, cp_block, blk_size);
|
||||
|
||||
/* Sanity checking of checkpoint */
|
||||
if (sanity_check_ckpt(sbi))
|
||||
goto fail_no_cp;
|
||||
|
||||
if (cp_blks <= 1)
|
||||
goto done;
|
||||
|
||||
|
|
@ -902,7 +920,7 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi)
|
|||
if (!get_pages(sbi, F2FS_WRITEBACK))
|
||||
break;
|
||||
|
||||
io_schedule();
|
||||
io_schedule_timeout(5*HZ);
|
||||
}
|
||||
finish_wait(&sbi->cp_wait, &wait);
|
||||
}
|
||||
|
|
@ -921,6 +939,9 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
int cp_payload_blks = __cp_payload(sbi);
|
||||
block_t discard_blk = NEXT_FREE_BLKADDR(sbi, curseg);
|
||||
bool invalidate = false;
|
||||
struct super_block *sb = sbi->sb;
|
||||
struct curseg_info *seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE);
|
||||
u64 kbytes_written;
|
||||
|
||||
/*
|
||||
* This avoids to conduct wrong roll-forward operations and uses
|
||||
|
|
@ -1008,7 +1029,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP));
|
||||
get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_BITMAP));
|
||||
|
||||
crc32 = f2fs_crc32(ckpt, le32_to_cpu(ckpt->checksum_offset));
|
||||
crc32 = f2fs_crc32(sbi, ckpt, le32_to_cpu(ckpt->checksum_offset));
|
||||
*((__le32 *)((unsigned char *)ckpt +
|
||||
le32_to_cpu(ckpt->checksum_offset)))
|
||||
= cpu_to_le32(crc32);
|
||||
|
|
@ -1034,6 +1055,14 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
|
||||
write_data_summaries(sbi, start_blk);
|
||||
start_blk += data_sum_blocks;
|
||||
|
||||
/* Record write statistics in the hot node summary */
|
||||
kbytes_written = sbi->kbytes_written;
|
||||
if (sb->s_bdev->bd_part)
|
||||
kbytes_written += BD_PART_WRITTEN(sbi);
|
||||
|
||||
seg_i->journal->info.kbytes_written = cpu_to_le64(kbytes_written);
|
||||
|
||||
if (__remain_node_summaries(cpc->reason)) {
|
||||
write_node_summaries(sbi, start_blk);
|
||||
start_blk += NR_CURSEG_NODE_TYPE;
|
||||
|
|
@ -1048,8 +1077,8 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return -EIO;
|
||||
|
||||
filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LONG_MAX);
|
||||
filemap_fdatawait_range(META_MAPPING(sbi), 0, LONG_MAX);
|
||||
filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LLONG_MAX);
|
||||
filemap_fdatawait_range(META_MAPPING(sbi), 0, LLONG_MAX);
|
||||
|
||||
/* update user_block_counts */
|
||||
sbi->last_valid_block_count = sbi->total_valid_block_count;
|
||||
|
|
@ -1112,9 +1141,7 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
|
||||
trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops");
|
||||
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
f2fs_submit_merged_bio(sbi, NODE, WRITE);
|
||||
f2fs_submit_merged_bio(sbi, META, WRITE);
|
||||
f2fs_flush_merged_bios(sbi);
|
||||
|
||||
/*
|
||||
* update checkpoint pack index
|
||||
|
|
|
|||
489
fs/f2fs/crypto.c
489
fs/f2fs/crypto.c
|
|
@ -1,489 +0,0 @@
|
|||
/*
|
||||
* linux/fs/f2fs/crypto.c
|
||||
*
|
||||
* Copied from linux/fs/ext4/crypto.c
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
* Copyright (C) 2015, Motorola Mobility
|
||||
*
|
||||
* This contains encryption functions for f2fs
|
||||
*
|
||||
* Written by Michael Halcrow, 2014.
|
||||
*
|
||||
* Filename encryption additions
|
||||
* Uday Savagaonkar, 2014
|
||||
* Encryption policy handling additions
|
||||
* Ildar Muslukhov, 2014
|
||||
* Remove ext4_encrypted_zeroout(),
|
||||
* add f2fs_restore_and_release_control_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 <crypto/skcipher.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <linux/ecryptfs.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/spinlock_types.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/bio.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "xattr.h"
|
||||
|
||||
/* Encryption added and removed here! (L: */
|
||||
|
||||
static unsigned int num_prealloc_crypto_pages = 32;
|
||||
static unsigned int num_prealloc_crypto_ctxs = 128;
|
||||
|
||||
module_param(num_prealloc_crypto_pages, uint, 0444);
|
||||
MODULE_PARM_DESC(num_prealloc_crypto_pages,
|
||||
"Number of crypto pages to preallocate");
|
||||
module_param(num_prealloc_crypto_ctxs, uint, 0444);
|
||||
MODULE_PARM_DESC(num_prealloc_crypto_ctxs,
|
||||
"Number of crypto contexts to preallocate");
|
||||
|
||||
static mempool_t *f2fs_bounce_page_pool;
|
||||
|
||||
static LIST_HEAD(f2fs_free_crypto_ctxs);
|
||||
static DEFINE_SPINLOCK(f2fs_crypto_ctx_lock);
|
||||
|
||||
static struct workqueue_struct *f2fs_read_workqueue;
|
||||
static DEFINE_MUTEX(crypto_init);
|
||||
|
||||
static struct kmem_cache *f2fs_crypto_ctx_cachep;
|
||||
struct kmem_cache *f2fs_crypt_info_cachep;
|
||||
|
||||
/**
|
||||
* f2fs_release_crypto_ctx() - Releases an encryption context
|
||||
* @ctx: The encryption context to release.
|
||||
*
|
||||
* If the encryption context was allocated from the pre-allocated pool, returns
|
||||
* it to that pool. Else, frees it.
|
||||
*
|
||||
* If there's a bounce page in the context, this frees that.
|
||||
*/
|
||||
void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (ctx->flags & F2FS_WRITE_PATH_FL && ctx->w.bounce_page) {
|
||||
mempool_free(ctx->w.bounce_page, f2fs_bounce_page_pool);
|
||||
ctx->w.bounce_page = NULL;
|
||||
}
|
||||
ctx->w.control_page = NULL;
|
||||
if (ctx->flags & F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL) {
|
||||
kmem_cache_free(f2fs_crypto_ctx_cachep, ctx);
|
||||
} else {
|
||||
spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags);
|
||||
list_add(&ctx->free_list, &f2fs_free_crypto_ctxs);
|
||||
spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_get_crypto_ctx() - Gets an encryption context
|
||||
* @inode: The inode for which we are doing the crypto
|
||||
*
|
||||
* Allocates and initializes an encryption context.
|
||||
*
|
||||
* Return: An allocated and initialized encryption context on success; error
|
||||
* value or NULL otherwise.
|
||||
*/
|
||||
struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode)
|
||||
{
|
||||
struct f2fs_crypto_ctx *ctx = NULL;
|
||||
unsigned long flags;
|
||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
||||
|
||||
if (ci == NULL)
|
||||
return ERR_PTR(-ENOKEY);
|
||||
|
||||
/*
|
||||
* We first try getting the ctx from a free list because in
|
||||
* the common case the ctx will have an allocated and
|
||||
* initialized crypto tfm, so it's probably a worthwhile
|
||||
* optimization. For the bounce page, we first try getting it
|
||||
* from the kernel allocator because that's just about as fast
|
||||
* as getting it from a list and because a cache of free pages
|
||||
* should generally be a "last resort" option for a filesystem
|
||||
* to be able to do its job.
|
||||
*/
|
||||
spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags);
|
||||
ctx = list_first_entry_or_null(&f2fs_free_crypto_ctxs,
|
||||
struct f2fs_crypto_ctx, free_list);
|
||||
if (ctx)
|
||||
list_del(&ctx->free_list);
|
||||
spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags);
|
||||
if (!ctx) {
|
||||
ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_NOFS);
|
||||
if (!ctx)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ctx->flags |= F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
|
||||
} else {
|
||||
ctx->flags &= ~F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
|
||||
}
|
||||
ctx->flags &= ~F2FS_WRITE_PATH_FL;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call f2fs_decrypt on every single page, reusing the encryption
|
||||
* context.
|
||||
*/
|
||||
static void completion_pages(struct work_struct *work)
|
||||
{
|
||||
struct f2fs_crypto_ctx *ctx =
|
||||
container_of(work, struct f2fs_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 = f2fs_decrypt(ctx, page);
|
||||
|
||||
if (ret) {
|
||||
WARN_ON_ONCE(1);
|
||||
SetPageError(page);
|
||||
} else
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
}
|
||||
f2fs_release_crypto_ctx(ctx);
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *ctx, struct bio *bio)
|
||||
{
|
||||
INIT_WORK(&ctx->r.work, completion_pages);
|
||||
ctx->r.bio = bio;
|
||||
queue_work(f2fs_read_workqueue, &ctx->r.work);
|
||||
}
|
||||
|
||||
static void f2fs_crypto_destroy(void)
|
||||
{
|
||||
struct f2fs_crypto_ctx *pos, *n;
|
||||
|
||||
list_for_each_entry_safe(pos, n, &f2fs_free_crypto_ctxs, free_list)
|
||||
kmem_cache_free(f2fs_crypto_ctx_cachep, pos);
|
||||
INIT_LIST_HEAD(&f2fs_free_crypto_ctxs);
|
||||
if (f2fs_bounce_page_pool)
|
||||
mempool_destroy(f2fs_bounce_page_pool);
|
||||
f2fs_bounce_page_pool = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_crypto_initialize() - Set up for f2fs encryption.
|
||||
*
|
||||
* 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 f2fs_crypto_initialize(void)
|
||||
{
|
||||
int i, res = -ENOMEM;
|
||||
|
||||
if (f2fs_bounce_page_pool)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&crypto_init);
|
||||
if (f2fs_bounce_page_pool)
|
||||
goto already_initialized;
|
||||
|
||||
for (i = 0; i < num_prealloc_crypto_ctxs; i++) {
|
||||
struct f2fs_crypto_ctx *ctx;
|
||||
|
||||
ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_KERNEL);
|
||||
if (!ctx)
|
||||
goto fail;
|
||||
list_add(&ctx->free_list, &f2fs_free_crypto_ctxs);
|
||||
}
|
||||
|
||||
/* must be allocated at the last step to avoid race condition above */
|
||||
f2fs_bounce_page_pool =
|
||||
mempool_create_page_pool(num_prealloc_crypto_pages, 0);
|
||||
if (!f2fs_bounce_page_pool)
|
||||
goto fail;
|
||||
|
||||
already_initialized:
|
||||
mutex_unlock(&crypto_init);
|
||||
return 0;
|
||||
fail:
|
||||
f2fs_crypto_destroy();
|
||||
mutex_unlock(&crypto_init);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_exit_crypto() - Shutdown the f2fs encryption system
|
||||
*/
|
||||
void f2fs_exit_crypto(void)
|
||||
{
|
||||
f2fs_crypto_destroy();
|
||||
|
||||
if (f2fs_read_workqueue)
|
||||
destroy_workqueue(f2fs_read_workqueue);
|
||||
if (f2fs_crypto_ctx_cachep)
|
||||
kmem_cache_destroy(f2fs_crypto_ctx_cachep);
|
||||
if (f2fs_crypt_info_cachep)
|
||||
kmem_cache_destroy(f2fs_crypt_info_cachep);
|
||||
}
|
||||
|
||||
int __init f2fs_init_crypto(void)
|
||||
{
|
||||
int res = -ENOMEM;
|
||||
|
||||
f2fs_read_workqueue = alloc_workqueue("f2fs_crypto", WQ_HIGHPRI, 0);
|
||||
if (!f2fs_read_workqueue)
|
||||
goto fail;
|
||||
|
||||
f2fs_crypto_ctx_cachep = KMEM_CACHE(f2fs_crypto_ctx,
|
||||
SLAB_RECLAIM_ACCOUNT);
|
||||
if (!f2fs_crypto_ctx_cachep)
|
||||
goto fail;
|
||||
|
||||
f2fs_crypt_info_cachep = KMEM_CACHE(f2fs_crypt_info,
|
||||
SLAB_RECLAIM_ACCOUNT);
|
||||
if (!f2fs_crypt_info_cachep)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
f2fs_exit_crypto();
|
||||
return res;
|
||||
}
|
||||
|
||||
void f2fs_restore_and_release_control_page(struct page **page)
|
||||
{
|
||||
struct f2fs_crypto_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 f2fs_crypto_ctx *)page_private(bounce_page);
|
||||
|
||||
/* restore control page */
|
||||
*page = ctx->w.control_page;
|
||||
|
||||
f2fs_restore_control_page(bounce_page);
|
||||
}
|
||||
|
||||
void f2fs_restore_control_page(struct page *data_page)
|
||||
{
|
||||
struct f2fs_crypto_ctx *ctx =
|
||||
(struct f2fs_crypto_ctx *)page_private(data_page);
|
||||
|
||||
set_page_private(data_page, (unsigned long)NULL);
|
||||
ClearPagePrivate(data_page);
|
||||
unlock_page(data_page);
|
||||
f2fs_release_crypto_ctx(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_crypt_complete() - The completion callback for page encryption
|
||||
* @req: The asynchronous encryption request context
|
||||
* @res: The result of the encryption operation
|
||||
*/
|
||||
static void f2fs_crypt_complete(struct crypto_async_request *req, int res)
|
||||
{
|
||||
struct f2fs_completion_result *ecr = req->data;
|
||||
|
||||
if (res == -EINPROGRESS)
|
||||
return;
|
||||
ecr->res = res;
|
||||
complete(&ecr->completion);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
F2FS_DECRYPT = 0,
|
||||
F2FS_ENCRYPT,
|
||||
} f2fs_direction_t;
|
||||
|
||||
static int f2fs_page_crypto(struct f2fs_crypto_ctx *ctx,
|
||||
struct inode *inode,
|
||||
f2fs_direction_t rw,
|
||||
pgoff_t index,
|
||||
struct page *src_page,
|
||||
struct page *dest_page)
|
||||
{
|
||||
u8 xts_tweak[F2FS_XTS_TWEAK_SIZE];
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_F2FS_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist dst, src;
|
||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
||||
struct crypto_skcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
|
||||
req = skcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_request_alloc() failed\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
skcipher_request_set_callback(
|
||||
req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
f2fs_crypt_complete, &ecr);
|
||||
|
||||
BUILD_BUG_ON(F2FS_XTS_TWEAK_SIZE < sizeof(index));
|
||||
memcpy(xts_tweak, &index, sizeof(index));
|
||||
memset(&xts_tweak[sizeof(index)], 0,
|
||||
F2FS_XTS_TWEAK_SIZE - sizeof(index));
|
||||
|
||||
sg_init_table(&dst, 1);
|
||||
sg_set_page(&dst, dest_page, PAGE_CACHE_SIZE, 0);
|
||||
sg_init_table(&src, 1);
|
||||
sg_set_page(&src, src_page, PAGE_CACHE_SIZE, 0);
|
||||
skcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE,
|
||||
xts_tweak);
|
||||
if (rw == F2FS_DECRYPT)
|
||||
res = crypto_skcipher_decrypt(req);
|
||||
else
|
||||
res = crypto_skcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
BUG_ON(req->base.data != &ecr);
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
skcipher_request_free(req);
|
||||
if (res) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_skcipher_encrypt() returned %d\n",
|
||||
__func__, res);
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct page *alloc_bounce_page(struct f2fs_crypto_ctx *ctx)
|
||||
{
|
||||
ctx->w.bounce_page = mempool_alloc(f2fs_bounce_page_pool, GFP_NOWAIT);
|
||||
if (ctx->w.bounce_page == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ctx->flags |= F2FS_WRITE_PATH_FL;
|
||||
return ctx->w.bounce_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_encrypt() - Encrypts a page
|
||||
* @inode: The inode for which the encryption should take place
|
||||
* @plaintext_page: The page to encrypt. Must be locked.
|
||||
*
|
||||
* Allocates a ciphertext page and encrypts plaintext_page into it using the ctx
|
||||
* encryption context.
|
||||
*
|
||||
* Called on the page write path. The caller must call
|
||||
* f2fs_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
|
||||
* error value or NULL.
|
||||
*/
|
||||
struct page *f2fs_encrypt(struct inode *inode,
|
||||
struct page *plaintext_page)
|
||||
{
|
||||
struct f2fs_crypto_ctx *ctx;
|
||||
struct page *ciphertext_page = NULL;
|
||||
int err;
|
||||
|
||||
BUG_ON(!PageLocked(plaintext_page));
|
||||
|
||||
ctx = f2fs_get_crypto_ctx(inode);
|
||||
if (IS_ERR(ctx))
|
||||
return (struct page *)ctx;
|
||||
|
||||
/* The encryption operation will require a bounce page. */
|
||||
ciphertext_page = alloc_bounce_page(ctx);
|
||||
if (IS_ERR(ciphertext_page))
|
||||
goto err_out;
|
||||
|
||||
ctx->w.control_page = plaintext_page;
|
||||
err = f2fs_page_crypto(ctx, inode, F2FS_ENCRYPT, plaintext_page->index,
|
||||
plaintext_page, ciphertext_page);
|
||||
if (err) {
|
||||
ciphertext_page = ERR_PTR(err);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
SetPagePrivate(ciphertext_page);
|
||||
set_page_private(ciphertext_page, (unsigned long)ctx);
|
||||
lock_page(ciphertext_page);
|
||||
return ciphertext_page;
|
||||
|
||||
err_out:
|
||||
f2fs_release_crypto_ctx(ctx);
|
||||
return ciphertext_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_decrypt() - Decrypts a page in-place
|
||||
* @ctx: The encryption context.
|
||||
* @page: The page to decrypt. Must be locked.
|
||||
*
|
||||
* Decrypts page in-place using the ctx encryption context.
|
||||
*
|
||||
* Called from the read completion callback.
|
||||
*
|
||||
* Return: Zero on success, non-zero otherwise.
|
||||
*/
|
||||
int f2fs_decrypt(struct f2fs_crypto_ctx *ctx, struct page *page)
|
||||
{
|
||||
BUG_ON(!PageLocked(page));
|
||||
|
||||
return f2fs_page_crypto(ctx, page->mapping->host,
|
||||
F2FS_DECRYPT, page->index, page, page);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience function which takes care of allocating and
|
||||
* deallocating the encryption context
|
||||
*/
|
||||
int f2fs_decrypt_one(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct f2fs_crypto_ctx *ctx = f2fs_get_crypto_ctx(inode);
|
||||
int ret;
|
||||
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
ret = f2fs_decrypt(ctx, page);
|
||||
f2fs_release_crypto_ctx(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool f2fs_valid_contents_enc_mode(uint32_t mode)
|
||||
{
|
||||
return (mode == F2FS_ENCRYPTION_MODE_AES_256_XTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_validate_encryption_key_size() - Validate the encryption key size
|
||||
* @mode: The key mode.
|
||||
* @size: The key size to validate.
|
||||
*
|
||||
* Return: The validated key size for @mode. Zero if invalid.
|
||||
*/
|
||||
uint32_t f2fs_validate_encryption_key_size(uint32_t mode, uint32_t size)
|
||||
{
|
||||
if (size == f2fs_encryption_key_size(mode))
|
||||
return size;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,250 +0,0 @@
|
|||
/*
|
||||
* linux/fs/f2fs/crypto_key.c
|
||||
*
|
||||
* Copied from linux/fs/f2fs/crypto_key.c
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains encryption key functions for f2fs
|
||||
*
|
||||
* Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
|
||||
*/
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <uapi/linux/keyctl.h>
|
||||
#include <crypto/skcipher.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "xattr.h"
|
||||
|
||||
static void derive_crypt_complete(struct crypto_async_request *req, int rc)
|
||||
{
|
||||
struct f2fs_completion_result *ecr = req->data;
|
||||
|
||||
if (rc == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
ecr->res = rc;
|
||||
complete(&ecr->completion);
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_derive_key_aes() - Derive a key using AES-128-ECB
|
||||
* @deriving_key: Encryption key used for derivatio.
|
||||
* @source_key: Source key to which to apply derivation.
|
||||
* @derived_key: Derived key.
|
||||
*
|
||||
* Return: Zero on success; non-zero otherwise.
|
||||
*/
|
||||
static int f2fs_derive_key_aes(char deriving_key[F2FS_AES_128_ECB_KEY_SIZE],
|
||||
char source_key[F2FS_AES_256_XTS_KEY_SIZE],
|
||||
char derived_key[F2FS_AES_256_XTS_KEY_SIZE])
|
||||
{
|
||||
int res = 0;
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_F2FS_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,
|
||||
F2FS_AES_128_ECB_KEY_SIZE);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
sg_init_one(&src_sg, source_key, F2FS_AES_256_XTS_KEY_SIZE);
|
||||
sg_init_one(&dst_sg, derived_key, F2FS_AES_256_XTS_KEY_SIZE);
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg,
|
||||
F2FS_AES_256_XTS_KEY_SIZE, NULL);
|
||||
res = crypto_skcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
BUG_ON(req->base.data != &ecr);
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
out:
|
||||
skcipher_request_free(req);
|
||||
crypto_free_skcipher(tfm);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void f2fs_free_crypt_info(struct f2fs_crypt_info *ci)
|
||||
{
|
||||
if (!ci)
|
||||
return;
|
||||
|
||||
key_put(ci->ci_keyring_key);
|
||||
crypto_free_skcipher(ci->ci_ctfm);
|
||||
kmem_cache_free(f2fs_crypt_info_cachep, ci);
|
||||
}
|
||||
|
||||
void f2fs_free_encryption_info(struct inode *inode, struct f2fs_crypt_info *ci)
|
||||
{
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
struct f2fs_crypt_info *prev;
|
||||
|
||||
if (ci == NULL)
|
||||
ci = ACCESS_ONCE(fi->i_crypt_info);
|
||||
if (ci == NULL)
|
||||
return;
|
||||
prev = cmpxchg(&fi->i_crypt_info, ci, NULL);
|
||||
if (prev != ci)
|
||||
return;
|
||||
|
||||
f2fs_free_crypt_info(ci);
|
||||
}
|
||||
|
||||
int _f2fs_get_encryption_info(struct inode *inode)
|
||||
{
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
struct f2fs_crypt_info *crypt_info;
|
||||
char full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE +
|
||||
(F2FS_KEY_DESCRIPTOR_SIZE * 2) + 1];
|
||||
struct key *keyring_key = NULL;
|
||||
struct f2fs_encryption_key *master_key;
|
||||
struct f2fs_encryption_context ctx;
|
||||
const struct user_key_payload *ukp;
|
||||
struct crypto_skcipher *ctfm;
|
||||
const char *cipher_str;
|
||||
char raw_key[F2FS_MAX_KEY_SIZE];
|
||||
char mode;
|
||||
int res;
|
||||
|
||||
res = f2fs_crypto_initialize();
|
||||
if (res)
|
||||
return res;
|
||||
retry:
|
||||
crypt_info = ACCESS_ONCE(fi->i_crypt_info);
|
||||
if (crypt_info) {
|
||||
if (!crypt_info->ci_keyring_key ||
|
||||
key_validate(crypt_info->ci_keyring_key) == 0)
|
||||
return 0;
|
||||
f2fs_free_encryption_info(inode, crypt_info);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
|
||||
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
&ctx, sizeof(ctx), NULL);
|
||||
if (res < 0)
|
||||
return res;
|
||||
else if (res != sizeof(ctx))
|
||||
return -EINVAL;
|
||||
res = 0;
|
||||
|
||||
crypt_info = kmem_cache_alloc(f2fs_crypt_info_cachep, GFP_NOFS);
|
||||
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 F2FS_ENCRYPTION_MODE_AES_256_XTS:
|
||||
cipher_str = "xts(aes)";
|
||||
break;
|
||||
case F2FS_ENCRYPTION_MODE_AES_256_CTS:
|
||||
cipher_str = "cts(cbc(aes))";
|
||||
break;
|
||||
default:
|
||||
printk_once(KERN_WARNING
|
||||
"f2fs: unsupported key mode %d (ino %u)\n",
|
||||
mode, (unsigned) inode->i_ino);
|
||||
res = -ENOKEY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(full_key_descriptor, F2FS_KEY_DESC_PREFIX,
|
||||
F2FS_KEY_DESC_PREFIX_SIZE);
|
||||
sprintf(full_key_descriptor + F2FS_KEY_DESC_PREFIX_SIZE,
|
||||
"%*phN", F2FS_KEY_DESCRIPTOR_SIZE,
|
||||
ctx.master_key_descriptor);
|
||||
full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE +
|
||||
(2 * F2FS_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;
|
||||
BUG_ON(keyring_key->type != &key_type_logon);
|
||||
ukp = user_key_payload(keyring_key);
|
||||
if (ukp->datalen != sizeof(struct f2fs_encryption_key)) {
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
master_key = (struct f2fs_encryption_key *)ukp->data;
|
||||
BUILD_BUG_ON(F2FS_AES_128_ECB_KEY_SIZE !=
|
||||
F2FS_KEY_DERIVATION_NONCE_SIZE);
|
||||
BUG_ON(master_key->size != F2FS_AES_256_XTS_KEY_SIZE);
|
||||
res = f2fs_derive_key_aes(ctx.nonce, master_key->raw,
|
||||
raw_key);
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
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_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
|
||||
res = crypto_skcipher_setkey(ctfm, raw_key,
|
||||
f2fs_encryption_key_size(mode));
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
memzero_explicit(raw_key, sizeof(raw_key));
|
||||
if (cmpxchg(&fi->i_crypt_info, NULL, crypt_info) != NULL) {
|
||||
f2fs_free_crypt_info(crypt_info);
|
||||
goto retry;
|
||||
}
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (res == -ENOKEY && !S_ISREG(inode->i_mode))
|
||||
res = 0;
|
||||
|
||||
f2fs_free_crypt_info(crypt_info);
|
||||
memzero_explicit(raw_key, sizeof(raw_key));
|
||||
return res;
|
||||
}
|
||||
|
||||
int f2fs_has_encryption_key(struct inode *inode)
|
||||
{
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
|
||||
return (fi->i_crypt_info != NULL);
|
||||
}
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
/*
|
||||
* copied from linux/fs/ext4/crypto_policy.c
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
* Copyright (C) 2015, Motorola Mobility.
|
||||
*
|
||||
* This contains encryption policy functions for f2fs with some modifications
|
||||
* to support f2fs-specific xattr APIs.
|
||||
*
|
||||
* Written by Michael Halcrow, 2015.
|
||||
* Modified by Jaegeuk Kim, 2015.
|
||||
*/
|
||||
#include <linux/random.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "xattr.h"
|
||||
|
||||
static int f2fs_inode_has_encryption_context(struct inode *inode)
|
||||
{
|
||||
int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
|
||||
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0, NULL);
|
||||
return (res > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* check whether the policy is consistent with the encryption context
|
||||
* for the inode
|
||||
*/
|
||||
static int f2fs_is_encryption_context_consistent_with_policy(
|
||||
struct inode *inode, const struct f2fs_encryption_policy *policy)
|
||||
{
|
||||
struct f2fs_encryption_context ctx;
|
||||
int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
|
||||
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
|
||||
sizeof(ctx), NULL);
|
||||
|
||||
if (res != sizeof(ctx))
|
||||
return 0;
|
||||
|
||||
return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
F2FS_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 f2fs_create_encryption_context_from_policy(
|
||||
struct inode *inode, const struct f2fs_encryption_policy *policy)
|
||||
{
|
||||
struct f2fs_encryption_context ctx;
|
||||
|
||||
ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
F2FS_KEY_DESCRIPTOR_SIZE);
|
||||
|
||||
if (!f2fs_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 (!f2fs_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 & ~F2FS_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) != F2FS_KEY_DERIVATION_NONCE_SIZE);
|
||||
get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE);
|
||||
|
||||
return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
|
||||
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
|
||||
sizeof(ctx), NULL, XATTR_CREATE);
|
||||
}
|
||||
|
||||
int f2fs_process_policy(const struct f2fs_encryption_policy *policy,
|
||||
struct inode *inode)
|
||||
{
|
||||
if (policy->version != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return -EINVAL;
|
||||
|
||||
if (!f2fs_inode_has_encryption_context(inode)) {
|
||||
if (!f2fs_empty_dir(inode))
|
||||
return -ENOTEMPTY;
|
||||
return f2fs_create_encryption_context_from_policy(inode,
|
||||
policy);
|
||||
}
|
||||
|
||||
if (f2fs_is_encryption_context_consistent_with_policy(inode, policy))
|
||||
return 0;
|
||||
|
||||
printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int f2fs_get_policy(struct inode *inode, struct f2fs_encryption_policy *policy)
|
||||
{
|
||||
struct f2fs_encryption_context ctx;
|
||||
int res;
|
||||
|
||||
if (!f2fs_encrypted_inode(inode))
|
||||
return -ENODATA;
|
||||
|
||||
res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
|
||||
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
&ctx, sizeof(ctx), NULL);
|
||||
if (res != sizeof(ctx))
|
||||
return -ENODATA;
|
||||
if (ctx.format != F2FS_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,
|
||||
F2FS_KEY_DESCRIPTOR_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int f2fs_is_child_context_consistent_with_parent(struct inode *parent,
|
||||
struct inode *child)
|
||||
{
|
||||
struct f2fs_crypt_info *parent_ci, *child_ci;
|
||||
int res;
|
||||
|
||||
if ((parent == NULL) || (child == NULL)) {
|
||||
pr_err("parent %p child %p\n", parent, child);
|
||||
BUG_ON(1);
|
||||
}
|
||||
|
||||
/* no restrictions if the parent directory is not encrypted */
|
||||
if (!f2fs_encrypted_inode(parent))
|
||||
return 1;
|
||||
/* if the child directory is not encrypted, this is always a problem */
|
||||
if (!f2fs_encrypted_inode(child))
|
||||
return 0;
|
||||
res = f2fs_get_encryption_info(parent);
|
||||
if (res)
|
||||
return 0;
|
||||
res = f2fs_get_encryption_info(child);
|
||||
if (res)
|
||||
return 0;
|
||||
parent_ci = F2FS_I(parent)->i_crypt_info;
|
||||
child_ci = F2FS_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,
|
||||
F2FS_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));
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_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 f2fs_inherit_context(struct inode *parent, struct inode *child,
|
||||
struct page *ipage)
|
||||
{
|
||||
struct f2fs_encryption_context ctx;
|
||||
struct f2fs_crypt_info *ci;
|
||||
int res;
|
||||
|
||||
res = f2fs_get_encryption_info(parent);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
ci = F2FS_I(parent)->i_crypt_info;
|
||||
BUG_ON(ci == NULL);
|
||||
|
||||
ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1;
|
||||
|
||||
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,
|
||||
F2FS_KEY_DESCRIPTOR_SIZE);
|
||||
|
||||
get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE);
|
||||
return f2fs_setxattr(child, F2FS_XATTR_INDEX_ENCRYPTION,
|
||||
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
|
||||
sizeof(ctx), ipage, XATTR_CREATE);
|
||||
}
|
||||
440
fs/f2fs/data.c
440
fs/f2fs/data.c
|
|
@ -34,9 +34,9 @@ static void f2fs_read_end_io(struct bio *bio)
|
|||
|
||||
if (f2fs_bio_encrypted(bio)) {
|
||||
if (bio->bi_error) {
|
||||
f2fs_release_crypto_ctx(bio->bi_private);
|
||||
fscrypt_release_ctx(bio->bi_private);
|
||||
} else {
|
||||
f2fs_end_io_crypto_work(bio->bi_private, bio);
|
||||
fscrypt_decrypt_bio_pages(bio->bi_private, bio);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -64,10 +64,9 @@ static void f2fs_write_end_io(struct bio *bio)
|
|||
bio_for_each_segment_all(bvec, bio, i) {
|
||||
struct page *page = bvec->bv_page;
|
||||
|
||||
f2fs_restore_and_release_control_page(&page);
|
||||
fscrypt_pullback_bio_page(&page, true);
|
||||
|
||||
if (unlikely(bio->bi_error)) {
|
||||
set_page_dirty(page);
|
||||
set_bit(AS_EIO, &page->mapping->flags);
|
||||
f2fs_stop_checkpoint(sbi);
|
||||
}
|
||||
|
|
@ -75,8 +74,7 @@ static void f2fs_write_end_io(struct bio *bio)
|
|||
dec_page_count(sbi, F2FS_WRITEBACK);
|
||||
}
|
||||
|
||||
if (!get_pages(sbi, F2FS_WRITEBACK) &&
|
||||
!list_empty(&sbi->cp_wait.task_list))
|
||||
if (!get_pages(sbi, F2FS_WRITEBACK) && wq_has_sleeper(&sbi->cp_wait))
|
||||
wake_up(&sbi->cp_wait);
|
||||
|
||||
bio_put(bio);
|
||||
|
|
@ -116,8 +114,54 @@ static void __submit_merged_bio(struct f2fs_bio_info *io)
|
|||
io->bio = NULL;
|
||||
}
|
||||
|
||||
void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
|
||||
enum page_type type, int rw)
|
||||
static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode,
|
||||
struct page *page, nid_t ino)
|
||||
{
|
||||
struct bio_vec *bvec;
|
||||
struct page *target;
|
||||
int i;
|
||||
|
||||
if (!io->bio)
|
||||
return false;
|
||||
|
||||
if (!inode && !page && !ino)
|
||||
return true;
|
||||
|
||||
bio_for_each_segment_all(bvec, io->bio, i) {
|
||||
|
||||
if (bvec->bv_page->mapping)
|
||||
target = bvec->bv_page;
|
||||
else
|
||||
target = fscrypt_control_page(bvec->bv_page);
|
||||
|
||||
if (inode && inode == target->mapping->host)
|
||||
return true;
|
||||
if (page && page == target)
|
||||
return true;
|
||||
if (ino && ino == ino_of_node(target))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool has_merged_page(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
struct page *page, nid_t ino,
|
||||
enum page_type type)
|
||||
{
|
||||
enum page_type btype = PAGE_TYPE_OF_BIO(type);
|
||||
struct f2fs_bio_info *io = &sbi->write_io[btype];
|
||||
bool ret;
|
||||
|
||||
down_read(&io->io_rwsem);
|
||||
ret = __has_merged_page(io, inode, page, ino);
|
||||
up_read(&io->io_rwsem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
|
||||
struct inode *inode, struct page *page,
|
||||
nid_t ino, enum page_type type, int rw)
|
||||
{
|
||||
enum page_type btype = PAGE_TYPE_OF_BIO(type);
|
||||
struct f2fs_bio_info *io;
|
||||
|
|
@ -126,6 +170,9 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
|
|||
|
||||
down_write(&io->io_rwsem);
|
||||
|
||||
if (!__has_merged_page(io, inode, page, ino))
|
||||
goto out;
|
||||
|
||||
/* change META to META_FLUSH in the checkpoint procedure */
|
||||
if (type >= META_FLUSH) {
|
||||
io->fio.type = META_FLUSH;
|
||||
|
|
@ -135,9 +182,31 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
|
|||
io->fio.rw = WRITE_FLUSH_FUA | REQ_META | REQ_PRIO;
|
||||
}
|
||||
__submit_merged_bio(io);
|
||||
out:
|
||||
up_write(&io->io_rwsem);
|
||||
}
|
||||
|
||||
void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, enum page_type type,
|
||||
int rw)
|
||||
{
|
||||
__f2fs_submit_merged_bio(sbi, NULL, NULL, 0, type, rw);
|
||||
}
|
||||
|
||||
void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *sbi,
|
||||
struct inode *inode, struct page *page,
|
||||
nid_t ino, enum page_type type, int rw)
|
||||
{
|
||||
if (has_merged_page(sbi, inode, page, ino, type))
|
||||
__f2fs_submit_merged_bio(sbi, inode, page, ino, type, rw);
|
||||
}
|
||||
|
||||
void f2fs_flush_merged_bios(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
f2fs_submit_merged_bio(sbi, NODE, WRITE);
|
||||
f2fs_submit_merged_bio(sbi, META, WRITE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill the locked page with data located in the block address.
|
||||
* Return unlocked page.
|
||||
|
|
@ -145,13 +214,14 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
|
|||
int f2fs_submit_page_bio(struct f2fs_io_info *fio)
|
||||
{
|
||||
struct bio *bio;
|
||||
struct page *page = fio->encrypted_page ? fio->encrypted_page : fio->page;
|
||||
struct page *page = fio->encrypted_page ?
|
||||
fio->encrypted_page : fio->page;
|
||||
|
||||
trace_f2fs_submit_page_bio(page, fio);
|
||||
f2fs_trace_ios(fio, 0);
|
||||
|
||||
/* Allocate a new bio */
|
||||
bio = __bio_alloc(fio->sbi, fio->blk_addr, 1, is_read_io(fio->rw));
|
||||
bio = __bio_alloc(fio->sbi, fio->new_blkaddr, 1, is_read_io(fio->rw));
|
||||
|
||||
if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) {
|
||||
bio_put(bio);
|
||||
|
|
@ -172,21 +242,24 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio)
|
|||
|
||||
io = is_read ? &sbi->read_io : &sbi->write_io[btype];
|
||||
|
||||
verify_block_addr(sbi, fio->blk_addr);
|
||||
if (fio->old_blkaddr != NEW_ADDR)
|
||||
verify_block_addr(sbi, fio->old_blkaddr);
|
||||
verify_block_addr(sbi, fio->new_blkaddr);
|
||||
|
||||
down_write(&io->io_rwsem);
|
||||
|
||||
if (!is_read)
|
||||
inc_page_count(sbi, F2FS_WRITEBACK);
|
||||
|
||||
if (io->bio && (io->last_block_in_bio != fio->blk_addr - 1 ||
|
||||
if (io->bio && (io->last_block_in_bio != fio->new_blkaddr - 1 ||
|
||||
io->fio.rw != fio->rw))
|
||||
__submit_merged_bio(io);
|
||||
alloc_new:
|
||||
if (io->bio == NULL) {
|
||||
int bio_blocks = MAX_BIO_BLOCKS(sbi);
|
||||
|
||||
io->bio = __bio_alloc(sbi, fio->blk_addr, bio_blocks, is_read);
|
||||
io->bio = __bio_alloc(sbi, fio->new_blkaddr,
|
||||
bio_blocks, is_read);
|
||||
io->fio = *fio;
|
||||
}
|
||||
|
||||
|
|
@ -198,7 +271,7 @@ alloc_new:
|
|||
goto alloc_new;
|
||||
}
|
||||
|
||||
io->last_block_in_bio = fio->blk_addr;
|
||||
io->last_block_in_bio = fio->new_blkaddr;
|
||||
f2fs_trace_ios(fio, 0);
|
||||
|
||||
up_write(&io->io_rwsem);
|
||||
|
|
@ -218,7 +291,7 @@ void set_data_blkaddr(struct dnode_of_data *dn)
|
|||
struct page *node_page = dn->node_page;
|
||||
unsigned int ofs_in_node = dn->ofs_in_node;
|
||||
|
||||
f2fs_wait_on_page_writeback(node_page, NODE);
|
||||
f2fs_wait_on_page_writeback(node_page, NODE, true);
|
||||
|
||||
rn = F2FS_NODE(node_page);
|
||||
|
||||
|
|
@ -229,6 +302,13 @@ void set_data_blkaddr(struct dnode_of_data *dn)
|
|||
dn->node_changed = true;
|
||||
}
|
||||
|
||||
void f2fs_update_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr)
|
||||
{
|
||||
dn->data_blkaddr = blkaddr;
|
||||
set_data_blkaddr(dn);
|
||||
f2fs_update_extent_cache(dn);
|
||||
}
|
||||
|
||||
int reserve_new_block(struct dnode_of_data *dn)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
|
||||
|
|
@ -332,7 +412,7 @@ got_it:
|
|||
return page;
|
||||
}
|
||||
|
||||
fio.blk_addr = dn.data_blkaddr;
|
||||
fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr;
|
||||
fio.page = page;
|
||||
err = f2fs_submit_page_bio(&fio);
|
||||
if (err)
|
||||
|
|
@ -461,7 +541,6 @@ got_it:
|
|||
static int __allocate_data_block(struct dnode_of_data *dn)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
|
||||
struct f2fs_inode_info *fi = F2FS_I(dn->inode);
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
int seg = CURSEG_WARM_DATA;
|
||||
|
|
@ -489,7 +568,7 @@ alloc:
|
|||
set_data_blkaddr(dn);
|
||||
|
||||
/* update i_size */
|
||||
fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
|
||||
fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) +
|
||||
dn->ofs_in_node;
|
||||
if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT))
|
||||
i_size_write(dn->inode,
|
||||
|
|
@ -497,67 +576,33 @@ alloc:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __allocate_data_blocks(struct inode *inode, loff_t offset,
|
||||
size_t count)
|
||||
ssize_t f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct dnode_of_data dn;
|
||||
u64 start = F2FS_BYTES_TO_BLK(offset);
|
||||
u64 len = F2FS_BYTES_TO_BLK(count);
|
||||
bool allocated;
|
||||
u64 end_offset;
|
||||
int err = 0;
|
||||
struct inode *inode = file_inode(iocb->ki_filp);
|
||||
struct f2fs_map_blocks map;
|
||||
ssize_t ret = 0;
|
||||
|
||||
while (len) {
|
||||
f2fs_lock_op(sbi);
|
||||
map.m_lblk = F2FS_BYTES_TO_BLK(iocb->ki_pos);
|
||||
map.m_len = F2FS_BLK_ALIGN(iov_iter_count(from));
|
||||
map.m_next_pgofs = NULL;
|
||||
|
||||
/* When reading holes, we need its node page */
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
err = get_dnode_of_data(&dn, start, ALLOC_NODE);
|
||||
if (err)
|
||||
goto out;
|
||||
if (f2fs_encrypted_inode(inode))
|
||||
return 0;
|
||||
|
||||
allocated = false;
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
|
||||
|
||||
while (dn.ofs_in_node < end_offset && len) {
|
||||
block_t blkaddr;
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
err = -EIO;
|
||||
goto sync_out;
|
||||
}
|
||||
|
||||
blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
|
||||
if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) {
|
||||
err = __allocate_data_block(&dn);
|
||||
if (err)
|
||||
goto sync_out;
|
||||
allocated = true;
|
||||
}
|
||||
len--;
|
||||
start++;
|
||||
dn.ofs_in_node++;
|
||||
}
|
||||
|
||||
if (allocated)
|
||||
sync_inode_page(&dn);
|
||||
|
||||
f2fs_put_dnode(&dn);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
f2fs_balance_fs(sbi, dn.node_changed);
|
||||
if (iocb->ki_flags & IOCB_DIRECT) {
|
||||
ret = f2fs_convert_inline_inode(inode);
|
||||
if (ret)
|
||||
return ret;
|
||||
return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_DIO);
|
||||
}
|
||||
return err;
|
||||
|
||||
sync_out:
|
||||
if (allocated)
|
||||
sync_inode_page(&dn);
|
||||
f2fs_put_dnode(&dn);
|
||||
out:
|
||||
f2fs_unlock_op(sbi);
|
||||
f2fs_balance_fs(sbi, dn.node_changed);
|
||||
return err;
|
||||
if (iocb->ki_pos + iov_iter_count(from) > MAX_INLINE_DATA) {
|
||||
ret = f2fs_convert_inline_inode(inode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (!f2fs_has_inline_data(inode))
|
||||
return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -588,13 +633,14 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
|
|||
/* it only supports block size == page size */
|
||||
pgofs = (pgoff_t)map->m_lblk;
|
||||
|
||||
if (f2fs_lookup_extent_cache(inode, pgofs, &ei)) {
|
||||
if (!create && f2fs_lookup_extent_cache(inode, pgofs, &ei)) {
|
||||
map->m_pblk = ei.blk + pgofs - ei.fofs;
|
||||
map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs);
|
||||
map->m_flags = F2FS_MAP_MAPPED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
next_dnode:
|
||||
if (create)
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
|
|
@ -602,74 +648,18 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
|
|||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
err = get_dnode_of_data(&dn, pgofs, mode);
|
||||
if (err) {
|
||||
if (err == -ENOENT)
|
||||
if (err == -ENOENT) {
|
||||
err = 0;
|
||||
if (map->m_next_pgofs)
|
||||
*map->m_next_pgofs =
|
||||
get_next_page_offset(&dn, pgofs);
|
||||
}
|
||||
goto unlock_out;
|
||||
}
|
||||
|
||||
if (dn.data_blkaddr == NEW_ADDR || dn.data_blkaddr == NULL_ADDR) {
|
||||
if (create) {
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
err = -EIO;
|
||||
goto put_out;
|
||||
}
|
||||
err = __allocate_data_block(&dn);
|
||||
if (err)
|
||||
goto put_out;
|
||||
allocated = true;
|
||||
map->m_flags = F2FS_MAP_NEW;
|
||||
} else {
|
||||
if (flag != F2FS_GET_BLOCK_FIEMAP ||
|
||||
dn.data_blkaddr != NEW_ADDR) {
|
||||
if (flag == F2FS_GET_BLOCK_BMAP)
|
||||
err = -ENOENT;
|
||||
goto put_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* preallocated unwritten block should be mapped
|
||||
* for fiemap.
|
||||
*/
|
||||
if (dn.data_blkaddr == NEW_ADDR)
|
||||
map->m_flags = F2FS_MAP_UNWRITTEN;
|
||||
}
|
||||
}
|
||||
|
||||
map->m_flags |= F2FS_MAP_MAPPED;
|
||||
map->m_pblk = dn.data_blkaddr;
|
||||
map->m_len = 1;
|
||||
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
|
||||
dn.ofs_in_node++;
|
||||
pgofs++;
|
||||
|
||||
get_next:
|
||||
if (map->m_len >= maxblocks)
|
||||
goto sync_out;
|
||||
|
||||
if (dn.ofs_in_node >= end_offset) {
|
||||
if (allocated)
|
||||
sync_inode_page(&dn);
|
||||
allocated = false;
|
||||
f2fs_put_dnode(&dn);
|
||||
|
||||
if (create) {
|
||||
f2fs_unlock_op(sbi);
|
||||
f2fs_balance_fs(sbi, dn.node_changed);
|
||||
f2fs_lock_op(sbi);
|
||||
}
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
err = get_dnode_of_data(&dn, pgofs, mode);
|
||||
if (err) {
|
||||
if (err == -ENOENT)
|
||||
err = 0;
|
||||
goto unlock_out;
|
||||
}
|
||||
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
|
||||
}
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
|
||||
|
||||
next_block:
|
||||
blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
|
||||
|
||||
if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) {
|
||||
|
|
@ -678,44 +668,78 @@ get_next:
|
|||
err = -EIO;
|
||||
goto sync_out;
|
||||
}
|
||||
err = __allocate_data_block(&dn);
|
||||
if (flag == F2FS_GET_BLOCK_PRE_AIO) {
|
||||
if (blkaddr == NULL_ADDR)
|
||||
err = reserve_new_block(&dn);
|
||||
} else {
|
||||
err = __allocate_data_block(&dn);
|
||||
}
|
||||
if (err)
|
||||
goto sync_out;
|
||||
allocated = true;
|
||||
map->m_flags |= F2FS_MAP_NEW;
|
||||
map->m_flags = F2FS_MAP_NEW;
|
||||
blkaddr = dn.data_blkaddr;
|
||||
} else {
|
||||
/*
|
||||
* we only merge preallocated unwritten blocks
|
||||
* for fiemap.
|
||||
*/
|
||||
if (flag == F2FS_GET_BLOCK_FIEMAP &&
|
||||
blkaddr == NULL_ADDR) {
|
||||
if (map->m_next_pgofs)
|
||||
*map->m_next_pgofs = pgofs + 1;
|
||||
}
|
||||
if (flag != F2FS_GET_BLOCK_FIEMAP ||
|
||||
blkaddr != NEW_ADDR)
|
||||
blkaddr != NEW_ADDR) {
|
||||
if (flag == F2FS_GET_BLOCK_BMAP)
|
||||
err = -ENOENT;
|
||||
goto sync_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Give more consecutive addresses for the readahead */
|
||||
if ((map->m_pblk != NEW_ADDR &&
|
||||
if (map->m_len == 0) {
|
||||
/* preallocated unwritten block should be mapped for fiemap. */
|
||||
if (blkaddr == NEW_ADDR)
|
||||
map->m_flags |= F2FS_MAP_UNWRITTEN;
|
||||
map->m_flags |= F2FS_MAP_MAPPED;
|
||||
|
||||
map->m_pblk = blkaddr;
|
||||
map->m_len = 1;
|
||||
} else if ((map->m_pblk != NEW_ADDR &&
|
||||
blkaddr == (map->m_pblk + ofs)) ||
|
||||
(map->m_pblk == NEW_ADDR &&
|
||||
blkaddr == NEW_ADDR)) {
|
||||
(map->m_pblk == NEW_ADDR && blkaddr == NEW_ADDR) ||
|
||||
flag == F2FS_GET_BLOCK_PRE_DIO ||
|
||||
flag == F2FS_GET_BLOCK_PRE_AIO) {
|
||||
ofs++;
|
||||
dn.ofs_in_node++;
|
||||
pgofs++;
|
||||
map->m_len++;
|
||||
goto get_next;
|
||||
} else {
|
||||
goto sync_out;
|
||||
}
|
||||
|
||||
dn.ofs_in_node++;
|
||||
pgofs++;
|
||||
|
||||
if (map->m_len < maxblocks) {
|
||||
if (dn.ofs_in_node < end_offset)
|
||||
goto next_block;
|
||||
|
||||
if (allocated)
|
||||
sync_inode_page(&dn);
|
||||
f2fs_put_dnode(&dn);
|
||||
|
||||
if (create) {
|
||||
f2fs_unlock_op(sbi);
|
||||
f2fs_balance_fs(sbi, allocated);
|
||||
}
|
||||
allocated = false;
|
||||
goto next_dnode;
|
||||
}
|
||||
|
||||
sync_out:
|
||||
if (allocated)
|
||||
sync_inode_page(&dn);
|
||||
put_out:
|
||||
f2fs_put_dnode(&dn);
|
||||
unlock_out:
|
||||
if (create) {
|
||||
f2fs_unlock_op(sbi);
|
||||
f2fs_balance_fs(sbi, dn.node_changed);
|
||||
f2fs_balance_fs(sbi, allocated);
|
||||
}
|
||||
out:
|
||||
trace_f2fs_map_blocks(inode, map, err);
|
||||
|
|
@ -723,13 +747,15 @@ out:
|
|||
}
|
||||
|
||||
static int __get_data_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh, int create, int flag)
|
||||
struct buffer_head *bh, int create, int flag,
|
||||
pgoff_t *next_pgofs)
|
||||
{
|
||||
struct f2fs_map_blocks map;
|
||||
int ret;
|
||||
|
||||
map.m_lblk = iblock;
|
||||
map.m_len = bh->b_size >> inode->i_blkbits;
|
||||
map.m_next_pgofs = next_pgofs;
|
||||
|
||||
ret = f2fs_map_blocks(inode, &map, create, flag);
|
||||
if (!ret) {
|
||||
|
|
@ -741,16 +767,18 @@ static int __get_data_block(struct inode *inode, sector_t iblock,
|
|||
}
|
||||
|
||||
static int get_data_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh_result, int create, int flag)
|
||||
struct buffer_head *bh_result, int create, int flag,
|
||||
pgoff_t *next_pgofs)
|
||||
{
|
||||
return __get_data_block(inode, iblock, bh_result, create, flag);
|
||||
return __get_data_block(inode, iblock, bh_result, create,
|
||||
flag, next_pgofs);
|
||||
}
|
||||
|
||||
static int get_data_block_dio(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh_result, int create)
|
||||
{
|
||||
return __get_data_block(inode, iblock, bh_result, create,
|
||||
F2FS_GET_BLOCK_DIO);
|
||||
F2FS_GET_BLOCK_DIO, NULL);
|
||||
}
|
||||
|
||||
static int get_data_block_bmap(struct inode *inode, sector_t iblock,
|
||||
|
|
@ -761,7 +789,7 @@ static int get_data_block_bmap(struct inode *inode, sector_t iblock,
|
|||
return -EFBIG;
|
||||
|
||||
return __get_data_block(inode, iblock, bh_result, create,
|
||||
F2FS_GET_BLOCK_BMAP);
|
||||
F2FS_GET_BLOCK_BMAP, NULL);
|
||||
}
|
||||
|
||||
static inline sector_t logical_to_blk(struct inode *inode, loff_t offset)
|
||||
|
|
@ -779,6 +807,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
|||
{
|
||||
struct buffer_head map_bh;
|
||||
sector_t start_blk, last_blk;
|
||||
pgoff_t next_pgofs;
|
||||
loff_t isize;
|
||||
u64 logical = 0, phys = 0, size = 0;
|
||||
u32 flags = 0;
|
||||
|
|
@ -814,14 +843,15 @@ next:
|
|||
map_bh.b_size = len;
|
||||
|
||||
ret = get_data_block(inode, start_blk, &map_bh, 0,
|
||||
F2FS_GET_BLOCK_FIEMAP);
|
||||
F2FS_GET_BLOCK_FIEMAP, &next_pgofs);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* HOLE */
|
||||
if (!buffer_mapped(&map_bh)) {
|
||||
start_blk = next_pgofs;
|
||||
/* Go through holes util pass the EOF */
|
||||
if (blk_to_logical(inode, start_blk++) < isize)
|
||||
if (blk_to_logical(inode, start_blk) < isize)
|
||||
goto prep_next;
|
||||
/* Found a hole beyond isize means no more extents.
|
||||
* Note that the premise is that filesystems don't
|
||||
|
|
@ -889,6 +919,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping,
|
|||
map.m_lblk = 0;
|
||||
map.m_len = 0;
|
||||
map.m_flags = 0;
|
||||
map.m_next_pgofs = NULL;
|
||||
|
||||
for (page_idx = 0; nr_pages; page_idx++, nr_pages--) {
|
||||
|
||||
|
|
@ -927,7 +958,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping,
|
|||
map.m_len = last_block - block_in_file;
|
||||
|
||||
if (f2fs_map_blocks(inode, &map, 0,
|
||||
F2FS_GET_BLOCK_READ))
|
||||
F2FS_GET_BLOCK_READ))
|
||||
goto set_error_page;
|
||||
}
|
||||
got_it:
|
||||
|
|
@ -956,12 +987,12 @@ submit_and_realloc:
|
|||
bio = NULL;
|
||||
}
|
||||
if (bio == NULL) {
|
||||
struct f2fs_crypto_ctx *ctx = NULL;
|
||||
struct fscrypt_ctx *ctx = NULL;
|
||||
|
||||
if (f2fs_encrypted_inode(inode) &&
|
||||
S_ISREG(inode->i_mode)) {
|
||||
|
||||
ctx = f2fs_get_crypto_ctx(inode);
|
||||
ctx = fscrypt_get_ctx(inode);
|
||||
if (IS_ERR(ctx))
|
||||
goto set_error_page;
|
||||
|
||||
|
|
@ -974,7 +1005,7 @@ submit_and_realloc:
|
|||
min_t(int, nr_pages, BIO_MAX_PAGES));
|
||||
if (!bio) {
|
||||
if (ctx)
|
||||
f2fs_release_crypto_ctx(ctx);
|
||||
fscrypt_release_ctx(ctx);
|
||||
goto set_error_page;
|
||||
}
|
||||
bio->bi_bdev = bdev;
|
||||
|
|
@ -1052,10 +1083,10 @@ int do_write_data_page(struct f2fs_io_info *fio)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
fio->blk_addr = dn.data_blkaddr;
|
||||
fio->old_blkaddr = dn.data_blkaddr;
|
||||
|
||||
/* This page is already truncated */
|
||||
if (fio->blk_addr == NULL_ADDR) {
|
||||
if (fio->old_blkaddr == NULL_ADDR) {
|
||||
ClearPageUptodate(page);
|
||||
goto out_writepage;
|
||||
}
|
||||
|
|
@ -1064,9 +1095,9 @@ int do_write_data_page(struct f2fs_io_info *fio)
|
|||
|
||||
/* wait for GCed encrypted page writeback */
|
||||
f2fs_wait_on_encrypted_page_writeback(F2FS_I_SB(inode),
|
||||
fio->blk_addr);
|
||||
fio->old_blkaddr);
|
||||
|
||||
fio->encrypted_page = f2fs_encrypt(inode, fio->page);
|
||||
fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page);
|
||||
if (IS_ERR(fio->encrypted_page)) {
|
||||
err = PTR_ERR(fio->encrypted_page);
|
||||
goto out_writepage;
|
||||
|
|
@ -1079,7 +1110,7 @@ int do_write_data_page(struct f2fs_io_info *fio)
|
|||
* If current allocation needs SSR,
|
||||
* it had better in-place writes for updated data.
|
||||
*/
|
||||
if (unlikely(fio->blk_addr != NEW_ADDR &&
|
||||
if (unlikely(fio->old_blkaddr != NEW_ADDR &&
|
||||
!is_cold_data(page) &&
|
||||
!IS_ATOMIC_WRITTEN_PAGE(page) &&
|
||||
need_inplace_update(inode))) {
|
||||
|
|
@ -1088,8 +1119,6 @@ int do_write_data_page(struct f2fs_io_info *fio)
|
|||
trace_f2fs_do_write_data_page(page, IPU);
|
||||
} else {
|
||||
write_data_page(&dn, fio);
|
||||
set_data_blkaddr(&dn);
|
||||
f2fs_update_extent_cache(&dn);
|
||||
trace_f2fs_do_write_data_page(page, OPU);
|
||||
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
|
||||
if (page->index == 0)
|
||||
|
|
@ -1177,12 +1206,18 @@ out:
|
|||
inode_dec_dirty_pages(inode);
|
||||
if (err)
|
||||
ClearPageUptodate(page);
|
||||
unlock_page(page);
|
||||
f2fs_balance_fs(sbi, need_balance_fs);
|
||||
if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) {
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
|
||||
if (wbc->for_reclaim) {
|
||||
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, DATA, WRITE);
|
||||
remove_dirty_inode(inode);
|
||||
}
|
||||
|
||||
unlock_page(page);
|
||||
f2fs_balance_fs(sbi, need_balance_fs);
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
|
||||
return 0;
|
||||
|
||||
redirty_out:
|
||||
|
|
@ -1282,7 +1317,8 @@ continue_unlock:
|
|||
|
||||
if (PageWriteback(page)) {
|
||||
if (wbc->sync_mode != WB_SYNC_NONE)
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_wait_on_page_writeback(page,
|
||||
DATA, true);
|
||||
else
|
||||
goto continue_unlock;
|
||||
}
|
||||
|
|
@ -1339,8 +1375,6 @@ static int f2fs_write_data_pages(struct address_space *mapping,
|
|||
int ret;
|
||||
long diff;
|
||||
|
||||
trace_f2fs_writepages(mapping->host, wbc, DATA);
|
||||
|
||||
/* deal with chardevs and other special file */
|
||||
if (!mapping->a_ops->writepage)
|
||||
return 0;
|
||||
|
|
@ -1362,14 +1396,16 @@ static int f2fs_write_data_pages(struct address_space *mapping,
|
|||
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||
goto skip_write;
|
||||
|
||||
trace_f2fs_writepages(mapping->host, wbc, DATA);
|
||||
|
||||
diff = nr_pages_to_write(sbi, DATA, wbc);
|
||||
|
||||
if (!S_ISDIR(inode->i_mode)) {
|
||||
if (!S_ISDIR(inode->i_mode) && wbc->sync_mode == WB_SYNC_ALL) {
|
||||
mutex_lock(&sbi->writepages);
|
||||
locked = true;
|
||||
}
|
||||
ret = f2fs_write_cache_pages(mapping, wbc, __f2fs_writepage, mapping);
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE);
|
||||
if (locked)
|
||||
mutex_unlock(&sbi->writepages);
|
||||
|
||||
|
|
@ -1380,6 +1416,7 @@ static int f2fs_write_data_pages(struct address_space *mapping,
|
|||
|
||||
skip_write:
|
||||
wbc->pages_skipped += get_dirty_pages(inode);
|
||||
trace_f2fs_writepages(mapping->host, wbc, DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1406,6 +1443,14 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
|
|||
struct extent_info ei;
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* we already allocated all the blocks, so we don't need to get
|
||||
* the block addresses when there is no need to fill the page.
|
||||
*/
|
||||
if (!f2fs_has_inline_data(inode) && !f2fs_encrypted_inode(inode) &&
|
||||
len == PAGE_CACHE_SIZE)
|
||||
return 0;
|
||||
|
||||
if (f2fs_has_inline_data(inode) ||
|
||||
(pos & PAGE_CACHE_MASK) >= i_size_read(inode)) {
|
||||
f2fs_lock_op(sbi);
|
||||
|
|
@ -1425,7 +1470,7 @@ restart:
|
|||
if (pos + len <= MAX_INLINE_DATA) {
|
||||
read_inline_data(page, ipage);
|
||||
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
|
||||
sync_inode_page(&dn);
|
||||
set_inline_node(ipage);
|
||||
} else {
|
||||
err = f2fs_convert_inline_page(&dn, page);
|
||||
if (err)
|
||||
|
|
@ -1439,13 +1484,9 @@ restart:
|
|||
if (f2fs_lookup_extent_cache(inode, index, &ei)) {
|
||||
dn.data_blkaddr = ei.blk + index - ei.fofs;
|
||||
} else {
|
||||
bool restart = false;
|
||||
|
||||
/* hole case */
|
||||
err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
|
||||
if (err || (!err && dn.data_blkaddr == NULL_ADDR))
|
||||
restart = true;
|
||||
if (restart) {
|
||||
if (err || (!err && dn.data_blkaddr == NULL_ADDR)) {
|
||||
f2fs_put_dnode(&dn);
|
||||
f2fs_lock_op(sbi);
|
||||
locked = true;
|
||||
|
|
@ -1514,7 +1555,7 @@ repeat:
|
|||
}
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_wait_on_page_writeback(page, DATA, false);
|
||||
|
||||
/* wait for GCed encrypted page writeback */
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
|
|
@ -1541,7 +1582,8 @@ repeat:
|
|||
.sbi = sbi,
|
||||
.type = DATA,
|
||||
.rw = READ_SYNC,
|
||||
.blk_addr = blkaddr,
|
||||
.old_blkaddr = blkaddr,
|
||||
.new_blkaddr = blkaddr,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
|
|
@ -1561,7 +1603,7 @@ repeat:
|
|||
|
||||
/* avoid symlink page */
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
|
||||
err = f2fs_decrypt_one(inode, page);
|
||||
err = fscrypt_decrypt_page(page);
|
||||
if (err)
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -1592,7 +1634,6 @@ static int f2fs_write_end(struct file *file,
|
|||
if (pos + copied > i_size_read(inode)) {
|
||||
i_size_write(inode, pos + copied);
|
||||
mark_inode_dirty(inode);
|
||||
update_inode_page(inode);
|
||||
}
|
||||
|
||||
f2fs_put_page(page, 1);
|
||||
|
|
@ -1617,34 +1658,21 @@ static int check_direct_IO(struct inode *inode, struct iov_iter *iter,
|
|||
static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
|
||||
loff_t offset)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
struct address_space *mapping = file->f_mapping;
|
||||
struct address_space *mapping = iocb->ki_filp->f_mapping;
|
||||
struct inode *inode = mapping->host;
|
||||
size_t count = iov_iter_count(iter);
|
||||
int err;
|
||||
|
||||
/* we don't need to use inline_data strictly */
|
||||
err = f2fs_convert_inline_inode(inode);
|
||||
err = check_direct_IO(inode, iter, offset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
return 0;
|
||||
|
||||
err = check_direct_IO(inode, iter, offset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
trace_f2fs_direct_IO_enter(inode, offset, count, iov_iter_rw(iter));
|
||||
|
||||
if (iov_iter_rw(iter) == WRITE) {
|
||||
err = __allocate_data_blocks(inode, offset, count);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = blockdev_direct_IO(iocb, inode, iter, offset, get_data_block_dio);
|
||||
out:
|
||||
if (err < 0 && iov_iter_rw(iter) == WRITE)
|
||||
f2fs_write_failed(mapping, offset + count);
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ static unsigned long dir_block_index(unsigned int level,
|
|||
}
|
||||
|
||||
static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
||||
struct f2fs_filename *fname,
|
||||
struct fscrypt_name *fname,
|
||||
f2fs_hash_t namehash,
|
||||
int *max_slots,
|
||||
struct page **res_page)
|
||||
|
|
@ -103,15 +103,15 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
|||
return de;
|
||||
}
|
||||
|
||||
struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname,
|
||||
struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname,
|
||||
f2fs_hash_t namehash, int *max_slots,
|
||||
struct f2fs_dentry_ptr *d)
|
||||
{
|
||||
struct f2fs_dir_entry *de;
|
||||
unsigned long bit_pos = 0;
|
||||
int max_len = 0;
|
||||
struct f2fs_str de_name = FSTR_INIT(NULL, 0);
|
||||
struct f2fs_str *name = &fname->disk_name;
|
||||
struct fscrypt_str de_name = FSTR_INIT(NULL, 0);
|
||||
struct fscrypt_str *name = &fname->disk_name;
|
||||
|
||||
if (max_slots)
|
||||
*max_slots = 0;
|
||||
|
|
@ -157,7 +157,7 @@ found:
|
|||
|
||||
static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
||||
unsigned int level,
|
||||
struct f2fs_filename *fname,
|
||||
struct fscrypt_name *fname,
|
||||
struct page **res_page)
|
||||
{
|
||||
struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
|
||||
|
|
@ -218,12 +218,12 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
|
|||
struct f2fs_dir_entry *de = NULL;
|
||||
unsigned int max_depth;
|
||||
unsigned int level;
|
||||
struct f2fs_filename fname;
|
||||
struct fscrypt_name fname;
|
||||
int err;
|
||||
|
||||
*res_page = NULL;
|
||||
|
||||
err = f2fs_fname_setup_filename(dir, child, 1, &fname);
|
||||
err = fscrypt_setup_filename(dir, child, 1, &fname);
|
||||
if (err)
|
||||
return NULL;
|
||||
|
||||
|
|
@ -251,7 +251,7 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
|
|||
break;
|
||||
}
|
||||
out:
|
||||
f2fs_fname_free_filename(&fname);
|
||||
fscrypt_free_filename(&fname);
|
||||
return de;
|
||||
}
|
||||
|
||||
|
|
@ -296,7 +296,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
|
|||
{
|
||||
enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA;
|
||||
lock_page(page);
|
||||
f2fs_wait_on_page_writeback(page, type);
|
||||
f2fs_wait_on_page_writeback(page, type, true);
|
||||
de->ino = cpu_to_le32(inode->i_ino);
|
||||
set_de_type(de, inode->i_mode);
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
|
|
@ -311,7 +311,7 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage)
|
|||
{
|
||||
struct f2fs_inode *ri;
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
/* copy name info. to this inode page */
|
||||
ri = F2FS_INODE(ipage);
|
||||
|
|
@ -341,24 +341,14 @@ int update_dent_inode(struct inode *inode, struct inode *to,
|
|||
void do_make_empty_dir(struct inode *inode, struct inode *parent,
|
||||
struct f2fs_dentry_ptr *d)
|
||||
{
|
||||
struct f2fs_dir_entry *de;
|
||||
struct qstr dot = QSTR_INIT(".", 1);
|
||||
struct qstr dotdot = QSTR_INIT("..", 2);
|
||||
|
||||
de = &d->dentry[0];
|
||||
de->name_len = cpu_to_le16(1);
|
||||
de->hash_code = 0;
|
||||
de->ino = cpu_to_le32(inode->i_ino);
|
||||
memcpy(d->filename[0], ".", 1);
|
||||
set_de_type(de, inode->i_mode);
|
||||
/* update dirent of "." */
|
||||
f2fs_update_dentry(inode->i_ino, inode->i_mode, d, &dot, 0, 0);
|
||||
|
||||
de = &d->dentry[1];
|
||||
de->hash_code = 0;
|
||||
de->name_len = cpu_to_le16(2);
|
||||
de->ino = cpu_to_le32(parent->i_ino);
|
||||
memcpy(d->filename[1], "..", 2);
|
||||
set_de_type(de, parent->i_mode);
|
||||
|
||||
test_and_set_bit_le(0, (void *)d->bitmap);
|
||||
test_and_set_bit_le(1, (void *)d->bitmap);
|
||||
/* update dirent of ".." */
|
||||
f2fs_update_dentry(parent->i_ino, parent->i_mode, d, &dotdot, 0, 1);
|
||||
}
|
||||
|
||||
static int make_empty_dir(struct inode *inode,
|
||||
|
|
@ -413,7 +403,7 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir,
|
|||
goto put_error;
|
||||
|
||||
if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) {
|
||||
err = f2fs_inherit_context(dir, inode, page);
|
||||
err = fscrypt_inherit_context(dir, inode, page, false);
|
||||
if (err)
|
||||
goto put_error;
|
||||
}
|
||||
|
|
@ -511,8 +501,12 @@ void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d,
|
|||
memcpy(d->filename[bit_pos], name->name, name->len);
|
||||
de->ino = cpu_to_le32(ino);
|
||||
set_de_type(de, mode);
|
||||
for (i = 0; i < slots; i++)
|
||||
for (i = 0; i < slots; i++) {
|
||||
test_and_set_bit_le(bit_pos + i, (void *)d->bitmap);
|
||||
/* avoid wrong garbage data for readdir */
|
||||
if (i)
|
||||
(de + i)->name_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -532,11 +526,11 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
|
|||
struct f2fs_dentry_block *dentry_blk = NULL;
|
||||
struct f2fs_dentry_ptr d;
|
||||
struct page *page = NULL;
|
||||
struct f2fs_filename fname;
|
||||
struct fscrypt_name fname;
|
||||
struct qstr new_name;
|
||||
int slots, err;
|
||||
|
||||
err = f2fs_fname_setup_filename(dir, name, 0, &fname);
|
||||
err = fscrypt_setup_filename(dir, name, 0, &fname);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -598,7 +592,7 @@ start:
|
|||
++level;
|
||||
goto start;
|
||||
add_dentry:
|
||||
f2fs_wait_on_page_writeback(dentry_page, DATA);
|
||||
f2fs_wait_on_page_writeback(dentry_page, DATA, true);
|
||||
|
||||
if (inode) {
|
||||
down_write(&F2FS_I(inode)->i_sem);
|
||||
|
|
@ -635,7 +629,7 @@ fail:
|
|||
kunmap(dentry_page);
|
||||
f2fs_put_page(dentry_page, 1);
|
||||
out:
|
||||
f2fs_fname_free_filename(&fname);
|
||||
fscrypt_free_filename(&fname);
|
||||
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -709,7 +703,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
|||
return f2fs_delete_inline_entry(dentry, page, dir, inode);
|
||||
|
||||
lock_page(page);
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
|
||||
dentry_blk = page_address(page);
|
||||
bit_pos = dentry - dentry_blk->dentry;
|
||||
|
|
@ -777,12 +771,12 @@ bool f2fs_empty_dir(struct inode *dir)
|
|||
}
|
||||
|
||||
bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
|
||||
unsigned int start_pos, struct f2fs_str *fstr)
|
||||
unsigned int start_pos, struct fscrypt_str *fstr)
|
||||
{
|
||||
unsigned char d_type = DT_UNKNOWN;
|
||||
unsigned int bit_pos;
|
||||
struct f2fs_dir_entry *de = NULL;
|
||||
struct f2fs_str de_name = FSTR_INIT(NULL, 0);
|
||||
struct fscrypt_str de_name = FSTR_INIT(NULL, 0);
|
||||
|
||||
bit_pos = ((unsigned long)ctx->pos % d->max);
|
||||
|
||||
|
|
@ -792,6 +786,12 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
|
|||
break;
|
||||
|
||||
de = &d->dentry[bit_pos];
|
||||
if (de->name_len == 0) {
|
||||
bit_pos++;
|
||||
ctx->pos = start_pos + bit_pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (de->file_type < F2FS_FT_MAX)
|
||||
d_type = f2fs_filetype_table[de->file_type];
|
||||
else
|
||||
|
|
@ -810,8 +810,9 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
|
|||
|
||||
memcpy(de_name.name, d->filename[bit_pos], de_name.len);
|
||||
|
||||
ret = f2fs_fname_disk_to_usr(d->inode, &de->hash_code,
|
||||
&de_name, fstr);
|
||||
ret = fscrypt_fname_disk_to_usr(d->inode,
|
||||
(u32)de->hash_code, 0,
|
||||
&de_name, fstr);
|
||||
kfree(de_name.name);
|
||||
if (ret < 0)
|
||||
return true;
|
||||
|
|
@ -839,16 +840,15 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
|
|||
struct file_ra_state *ra = &file->f_ra;
|
||||
unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK);
|
||||
struct f2fs_dentry_ptr d;
|
||||
struct f2fs_str fstr = FSTR_INIT(NULL, 0);
|
||||
struct fscrypt_str fstr = FSTR_INIT(NULL, 0);
|
||||
int err = 0;
|
||||
|
||||
if (f2fs_encrypted_inode(inode)) {
|
||||
err = f2fs_get_encryption_info(inode);
|
||||
if (err)
|
||||
err = fscrypt_get_encryption_info(inode);
|
||||
if (err && err != -ENOKEY)
|
||||
return err;
|
||||
|
||||
err = f2fs_fname_crypto_alloc_buffer(inode, F2FS_NAME_LEN,
|
||||
&fstr);
|
||||
err = fscrypt_fname_alloc_buffer(inode, F2FS_NAME_LEN, &fstr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
|
@ -888,15 +888,23 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
|
|||
f2fs_put_page(dentry_page, 1);
|
||||
}
|
||||
out:
|
||||
f2fs_fname_crypto_free_buffer(&fstr);
|
||||
fscrypt_fname_free_buffer(&fstr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_dir_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
if (f2fs_encrypted_inode(inode))
|
||||
return fscrypt_get_encryption_info(inode) ? -EACCES : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations f2fs_dir_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
.iterate = f2fs_readdir,
|
||||
.fsync = f2fs_sync_file,
|
||||
.open = f2fs_dir_open,
|
||||
.unlocked_ioctl = f2fs_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = f2fs_compat_ioctl,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi,
|
|||
|
||||
en->ei = *ei;
|
||||
INIT_LIST_HEAD(&en->list);
|
||||
en->et = et;
|
||||
|
||||
rb_link_node(&en->rb_node, parent, p);
|
||||
rb_insert_color(&en->rb_node, &et->root);
|
||||
|
|
@ -50,6 +51,24 @@ static void __detach_extent_node(struct f2fs_sb_info *sbi,
|
|||
|
||||
if (et->cached_en == en)
|
||||
et->cached_en = NULL;
|
||||
kmem_cache_free(extent_node_slab, en);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flow to release an extent_node:
|
||||
* 1. list_del_init
|
||||
* 2. __detach_extent_node
|
||||
* 3. kmem_cache_free.
|
||||
*/
|
||||
static void __release_extent_node(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_node *en)
|
||||
{
|
||||
spin_lock(&sbi->extent_lock);
|
||||
f2fs_bug_on(sbi, list_empty(&en->list));
|
||||
list_del_init(&en->list);
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
|
||||
__detach_extent_node(sbi, et, en);
|
||||
}
|
||||
|
||||
static struct extent_tree *__grab_extent_tree(struct inode *inode)
|
||||
|
|
@ -129,7 +148,7 @@ static struct extent_node *__init_extent_tree(struct f2fs_sb_info *sbi,
|
|||
}
|
||||
|
||||
static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, bool free_all)
|
||||
struct extent_tree *et)
|
||||
{
|
||||
struct rb_node *node, *next;
|
||||
struct extent_node *en;
|
||||
|
|
@ -139,18 +158,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
|
|||
while (node) {
|
||||
next = rb_next(node);
|
||||
en = rb_entry(node, struct extent_node, rb_node);
|
||||
|
||||
if (free_all) {
|
||||
spin_lock(&sbi->extent_lock);
|
||||
if (!list_empty(&en->list))
|
||||
list_del_init(&en->list);
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
}
|
||||
|
||||
if (free_all || list_empty(&en->list)) {
|
||||
__detach_extent_node(sbi, et, en);
|
||||
kmem_cache_free(extent_node_slab, en);
|
||||
}
|
||||
__release_extent_node(sbi, et, en);
|
||||
node = next;
|
||||
}
|
||||
|
||||
|
|
@ -232,9 +240,10 @@ static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
|
|||
if (en) {
|
||||
*ei = en->ei;
|
||||
spin_lock(&sbi->extent_lock);
|
||||
if (!list_empty(&en->list))
|
||||
if (!list_empty(&en->list)) {
|
||||
list_move_tail(&en->list, &sbi->extent_list);
|
||||
et->cached_en = en;
|
||||
et->cached_en = en;
|
||||
}
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
ret = true;
|
||||
}
|
||||
|
|
@ -329,7 +338,6 @@ lookup_neighbors:
|
|||
|
||||
static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_info *ei,
|
||||
struct extent_node **den,
|
||||
struct extent_node *prev_ex,
|
||||
struct extent_node *next_ex)
|
||||
{
|
||||
|
|
@ -342,20 +350,25 @@ static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi,
|
|||
}
|
||||
|
||||
if (next_ex && __is_front_mergeable(ei, &next_ex->ei)) {
|
||||
if (en) {
|
||||
__detach_extent_node(sbi, et, prev_ex);
|
||||
*den = prev_ex;
|
||||
}
|
||||
if (en)
|
||||
__release_extent_node(sbi, et, prev_ex);
|
||||
next_ex->ei.fofs = ei->fofs;
|
||||
next_ex->ei.blk = ei->blk;
|
||||
next_ex->ei.len += ei->len;
|
||||
en = next_ex;
|
||||
}
|
||||
|
||||
if (en) {
|
||||
__try_update_largest_extent(et, en);
|
||||
if (!en)
|
||||
return NULL;
|
||||
|
||||
__try_update_largest_extent(et, en);
|
||||
|
||||
spin_lock(&sbi->extent_lock);
|
||||
if (!list_empty(&en->list)) {
|
||||
list_move_tail(&en->list, &sbi->extent_list);
|
||||
et->cached_en = en;
|
||||
}
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
return en;
|
||||
}
|
||||
|
||||
|
|
@ -391,7 +404,12 @@ do_insert:
|
|||
return NULL;
|
||||
|
||||
__try_update_largest_extent(et, en);
|
||||
|
||||
/* update in global extent list */
|
||||
spin_lock(&sbi->extent_lock);
|
||||
list_add_tail(&en->list, &sbi->extent_list);
|
||||
et->cached_en = en;
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
return en;
|
||||
}
|
||||
|
||||
|
|
@ -479,7 +497,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
|||
if (parts)
|
||||
__try_update_largest_extent(et, en);
|
||||
else
|
||||
__detach_extent_node(sbi, et, en);
|
||||
__release_extent_node(sbi, et, en);
|
||||
|
||||
/*
|
||||
* if original extent is split into zero or two parts, extent
|
||||
|
|
@ -490,31 +508,15 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
|||
insert_p = NULL;
|
||||
insert_parent = NULL;
|
||||
}
|
||||
|
||||
/* update in global extent list */
|
||||
spin_lock(&sbi->extent_lock);
|
||||
if (!parts && !list_empty(&en->list))
|
||||
list_del(&en->list);
|
||||
if (en1)
|
||||
list_add_tail(&en1->list, &sbi->extent_list);
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
|
||||
/* release extent node */
|
||||
if (!parts)
|
||||
kmem_cache_free(extent_node_slab, en);
|
||||
|
||||
en = next_en;
|
||||
}
|
||||
|
||||
/* 3. update extent in extent cache */
|
||||
if (blkaddr) {
|
||||
struct extent_node *den = NULL;
|
||||
|
||||
set_extent_info(&ei, fofs, blkaddr, len);
|
||||
en1 = __try_merge_extent_node(sbi, et, &ei, &den,
|
||||
prev_en, next_en);
|
||||
if (!en1)
|
||||
en1 = __insert_extent_tree(sbi, et, &ei,
|
||||
if (!__try_merge_extent_node(sbi, et, &ei, prev_en, next_en))
|
||||
__insert_extent_tree(sbi, et, &ei,
|
||||
insert_p, insert_parent);
|
||||
|
||||
/* give up extent_cache, if split and small updates happen */
|
||||
|
|
@ -524,24 +526,10 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
|||
et->largest.len = 0;
|
||||
set_inode_flag(F2FS_I(inode), FI_NO_EXTENT);
|
||||
}
|
||||
|
||||
spin_lock(&sbi->extent_lock);
|
||||
if (en1) {
|
||||
if (list_empty(&en1->list))
|
||||
list_add_tail(&en1->list, &sbi->extent_list);
|
||||
else
|
||||
list_move_tail(&en1->list, &sbi->extent_list);
|
||||
}
|
||||
if (den && !list_empty(&den->list))
|
||||
list_del(&den->list);
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
|
||||
if (den)
|
||||
kmem_cache_free(extent_node_slab, den);
|
||||
}
|
||||
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT))
|
||||
__free_extent_tree(sbi, et, true);
|
||||
__free_extent_tree(sbi, et);
|
||||
|
||||
write_unlock(&et->lock);
|
||||
|
||||
|
|
@ -550,14 +538,10 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
|||
|
||||
unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
|
||||
{
|
||||
struct extent_tree *treevec[EXT_TREE_VEC_SIZE];
|
||||
struct extent_tree *et, *next;
|
||||
struct extent_node *en, *tmp;
|
||||
unsigned long ino = F2FS_ROOT_INO(sbi);
|
||||
unsigned int found;
|
||||
struct extent_node *en;
|
||||
unsigned int node_cnt = 0, tree_cnt = 0;
|
||||
int remained;
|
||||
bool do_free = false;
|
||||
|
||||
if (!test_opt(sbi, EXTENT_CACHE))
|
||||
return 0;
|
||||
|
|
@ -572,10 +556,10 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
|
|||
list_for_each_entry_safe(et, next, &sbi->zombie_list, list) {
|
||||
if (atomic_read(&et->node_cnt)) {
|
||||
write_lock(&et->lock);
|
||||
node_cnt += __free_extent_tree(sbi, et, true);
|
||||
node_cnt += __free_extent_tree(sbi, et);
|
||||
write_unlock(&et->lock);
|
||||
}
|
||||
|
||||
f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
|
||||
list_del_init(&et->list);
|
||||
radix_tree_delete(&sbi->extent_tree_root, et->ino);
|
||||
kmem_cache_free(extent_tree_slab, et);
|
||||
|
|
@ -585,6 +569,7 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
|
|||
|
||||
if (node_cnt + tree_cnt >= nr_shrink)
|
||||
goto unlock_out;
|
||||
cond_resched();
|
||||
}
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
|
||||
|
|
@ -596,42 +581,29 @@ free_node:
|
|||
remained = nr_shrink - (node_cnt + tree_cnt);
|
||||
|
||||
spin_lock(&sbi->extent_lock);
|
||||
list_for_each_entry_safe(en, tmp, &sbi->extent_list, list) {
|
||||
if (!remained--)
|
||||
for (; remained > 0; remained--) {
|
||||
if (list_empty(&sbi->extent_list))
|
||||
break;
|
||||
en = list_first_entry(&sbi->extent_list,
|
||||
struct extent_node, list);
|
||||
et = en->et;
|
||||
if (!write_trylock(&et->lock)) {
|
||||
/* refresh this extent node's position in extent list */
|
||||
list_move_tail(&en->list, &sbi->extent_list);
|
||||
continue;
|
||||
}
|
||||
|
||||
list_del_init(&en->list);
|
||||
do_free = true;
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
|
||||
__detach_extent_node(sbi, et, en);
|
||||
|
||||
write_unlock(&et->lock);
|
||||
node_cnt++;
|
||||
spin_lock(&sbi->extent_lock);
|
||||
}
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
|
||||
if (do_free == false)
|
||||
goto unlock_out;
|
||||
|
||||
/*
|
||||
* reset ino for searching victims from beginning of global extent tree.
|
||||
*/
|
||||
ino = F2FS_ROOT_INO(sbi);
|
||||
|
||||
while ((found = radix_tree_gang_lookup(&sbi->extent_tree_root,
|
||||
(void **)treevec, ino, EXT_TREE_VEC_SIZE))) {
|
||||
unsigned i;
|
||||
|
||||
ino = treevec[found - 1]->ino + 1;
|
||||
for (i = 0; i < found; i++) {
|
||||
struct extent_tree *et = treevec[i];
|
||||
|
||||
if (!atomic_read(&et->node_cnt))
|
||||
continue;
|
||||
|
||||
if (write_trylock(&et->lock)) {
|
||||
node_cnt += __free_extent_tree(sbi, et, false);
|
||||
write_unlock(&et->lock);
|
||||
}
|
||||
|
||||
if (node_cnt + tree_cnt >= nr_shrink)
|
||||
goto unlock_out;
|
||||
}
|
||||
}
|
||||
unlock_out:
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
out:
|
||||
|
|
@ -650,7 +622,7 @@ unsigned int f2fs_destroy_extent_node(struct inode *inode)
|
|||
return 0;
|
||||
|
||||
write_lock(&et->lock);
|
||||
node_cnt = __free_extent_tree(sbi, et, true);
|
||||
node_cnt = __free_extent_tree(sbi, et);
|
||||
write_unlock(&et->lock);
|
||||
|
||||
return node_cnt;
|
||||
|
|
@ -701,19 +673,21 @@ bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs,
|
|||
|
||||
void f2fs_update_extent_cache(struct dnode_of_data *dn)
|
||||
{
|
||||
struct f2fs_inode_info *fi = F2FS_I(dn->inode);
|
||||
pgoff_t fofs;
|
||||
block_t blkaddr;
|
||||
|
||||
if (!f2fs_may_extent_tree(dn->inode))
|
||||
return;
|
||||
|
||||
f2fs_bug_on(F2FS_I_SB(dn->inode), dn->data_blkaddr == NEW_ADDR);
|
||||
if (dn->data_blkaddr == NEW_ADDR)
|
||||
blkaddr = NULL_ADDR;
|
||||
else
|
||||
blkaddr = dn->data_blkaddr;
|
||||
|
||||
fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) +
|
||||
dn->ofs_in_node;
|
||||
|
||||
fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
|
||||
dn->ofs_in_node;
|
||||
|
||||
if (f2fs_update_extent_tree_range(dn->inode, fofs, dn->data_blkaddr, 1))
|
||||
if (f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, 1))
|
||||
sync_inode_page(dn);
|
||||
}
|
||||
|
||||
|
|
|
|||
315
fs/f2fs/f2fs.h
315
fs/f2fs/f2fs.h
|
|
@ -22,10 +22,11 @@
|
|||
#include <linux/vmalloc.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/fscrypto.h>
|
||||
#include <crypto/hash.h>
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
#define f2fs_bug_on(sbi, condition) BUG_ON(condition)
|
||||
#define f2fs_down_write(x, y) down_write_nest_lock(x, y)
|
||||
#else
|
||||
#define f2fs_bug_on(sbi, condition) \
|
||||
do { \
|
||||
|
|
@ -34,7 +35,6 @@
|
|||
set_sbi_flag(sbi, SBI_NEED_FSCK); \
|
||||
} \
|
||||
} while (0)
|
||||
#define f2fs_down_write(x, y) down_write(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
@ -84,27 +84,6 @@ struct f2fs_mount_info {
|
|||
#define F2FS_CLEAR_FEATURE(sb, mask) \
|
||||
F2FS_SB(sb)->raw_super->feature &= ~cpu_to_le32(mask)
|
||||
|
||||
#define CRCPOLY_LE 0xedb88320
|
||||
|
||||
static inline __u32 f2fs_crc32(void *buf, size_t len)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)buf;
|
||||
__u32 crc = F2FS_SUPER_MAGIC;
|
||||
int i;
|
||||
|
||||
while (len--) {
|
||||
crc ^= *p++;
|
||||
for (i = 0; i < 8; i++)
|
||||
crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static inline bool f2fs_crc_valid(__u32 blk_crc, void *buf, size_t buf_size)
|
||||
{
|
||||
return f2fs_crc32(buf, buf_size) == blk_crc;
|
||||
}
|
||||
|
||||
/*
|
||||
* For checkpoint manager
|
||||
*/
|
||||
|
|
@ -183,37 +162,37 @@ struct fsync_inode_entry {
|
|||
block_t last_inode; /* block address locating the last inode */
|
||||
};
|
||||
|
||||
#define nats_in_cursum(sum) (le16_to_cpu(sum->n_nats))
|
||||
#define sits_in_cursum(sum) (le16_to_cpu(sum->n_sits))
|
||||
#define nats_in_cursum(jnl) (le16_to_cpu(jnl->n_nats))
|
||||
#define sits_in_cursum(jnl) (le16_to_cpu(jnl->n_sits))
|
||||
|
||||
#define nat_in_journal(sum, i) (sum->nat_j.entries[i].ne)
|
||||
#define nid_in_journal(sum, i) (sum->nat_j.entries[i].nid)
|
||||
#define sit_in_journal(sum, i) (sum->sit_j.entries[i].se)
|
||||
#define segno_in_journal(sum, i) (sum->sit_j.entries[i].segno)
|
||||
#define nat_in_journal(jnl, i) (jnl->nat_j.entries[i].ne)
|
||||
#define nid_in_journal(jnl, i) (jnl->nat_j.entries[i].nid)
|
||||
#define sit_in_journal(jnl, i) (jnl->sit_j.entries[i].se)
|
||||
#define segno_in_journal(jnl, i) (jnl->sit_j.entries[i].segno)
|
||||
|
||||
#define MAX_NAT_JENTRIES(sum) (NAT_JOURNAL_ENTRIES - nats_in_cursum(sum))
|
||||
#define MAX_SIT_JENTRIES(sum) (SIT_JOURNAL_ENTRIES - sits_in_cursum(sum))
|
||||
#define MAX_NAT_JENTRIES(jnl) (NAT_JOURNAL_ENTRIES - nats_in_cursum(jnl))
|
||||
#define MAX_SIT_JENTRIES(jnl) (SIT_JOURNAL_ENTRIES - sits_in_cursum(jnl))
|
||||
|
||||
static inline int update_nats_in_cursum(struct f2fs_summary_block *rs, int i)
|
||||
static inline int update_nats_in_cursum(struct f2fs_journal *journal, int i)
|
||||
{
|
||||
int before = nats_in_cursum(rs);
|
||||
rs->n_nats = cpu_to_le16(before + i);
|
||||
int before = nats_in_cursum(journal);
|
||||
journal->n_nats = cpu_to_le16(before + i);
|
||||
return before;
|
||||
}
|
||||
|
||||
static inline int update_sits_in_cursum(struct f2fs_summary_block *rs, int i)
|
||||
static inline int update_sits_in_cursum(struct f2fs_journal *journal, int i)
|
||||
{
|
||||
int before = sits_in_cursum(rs);
|
||||
rs->n_sits = cpu_to_le16(before + i);
|
||||
int before = sits_in_cursum(journal);
|
||||
journal->n_sits = cpu_to_le16(before + i);
|
||||
return before;
|
||||
}
|
||||
|
||||
static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
|
||||
int type)
|
||||
static inline bool __has_cursum_space(struct f2fs_journal *journal,
|
||||
int size, int type)
|
||||
{
|
||||
if (type == NAT_JOURNAL)
|
||||
return size <= MAX_NAT_JENTRIES(sum);
|
||||
return size <= MAX_SIT_JENTRIES(sum);
|
||||
return size <= MAX_NAT_JENTRIES(journal);
|
||||
return size <= MAX_SIT_JENTRIES(journal);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -233,12 +212,9 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
|
|||
#define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7)
|
||||
#define F2FS_IOC_DEFRAGMENT _IO(F2FS_IOCTL_MAGIC, 8)
|
||||
|
||||
#define F2FS_IOC_SET_ENCRYPTION_POLICY \
|
||||
_IOR('f', 19, struct f2fs_encryption_policy)
|
||||
#define F2FS_IOC_GET_ENCRYPTION_PWSALT \
|
||||
_IOW('f', 20, __u8[16])
|
||||
#define F2FS_IOC_GET_ENCRYPTION_POLICY \
|
||||
_IOW('f', 21, struct f2fs_encryption_policy)
|
||||
#define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY
|
||||
#define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY
|
||||
#define F2FS_IOC_GET_ENCRYPTION_PWSALT FS_IOC_GET_ENCRYPTION_PWSALT
|
||||
|
||||
/*
|
||||
* should be same as XFS_IOC_GOINGDOWN.
|
||||
|
|
@ -268,25 +244,6 @@ struct f2fs_defragment {
|
|||
* For INODE and NODE manager
|
||||
*/
|
||||
/* for directory operations */
|
||||
struct f2fs_str {
|
||||
unsigned char *name;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
struct f2fs_filename {
|
||||
const struct qstr *usr_fname;
|
||||
struct f2fs_str disk_name;
|
||||
f2fs_hash_t hash;
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
struct f2fs_str crypto_buf;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define FSTR_INIT(n, l) { .name = n, .len = l }
|
||||
#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len)
|
||||
#define fname_name(p) ((p)->disk_name.name)
|
||||
#define fname_len(p) ((p)->disk_name.len)
|
||||
|
||||
struct f2fs_dentry_ptr {
|
||||
struct inode *inode;
|
||||
const void *bitmap;
|
||||
|
|
@ -354,6 +311,7 @@ struct extent_node {
|
|||
struct rb_node rb_node; /* rb node located in rb-tree */
|
||||
struct list_head list; /* node in global extent list of sbi */
|
||||
struct extent_info ei; /* extent info */
|
||||
struct extent_tree *et; /* extent tree pointer */
|
||||
};
|
||||
|
||||
struct extent_tree {
|
||||
|
|
@ -382,6 +340,7 @@ struct f2fs_map_blocks {
|
|||
block_t m_lblk;
|
||||
unsigned int m_len;
|
||||
unsigned int m_flags;
|
||||
pgoff_t *m_next_pgofs; /* point next possible non-hole pgofs */
|
||||
};
|
||||
|
||||
/* for flag in get_data_block */
|
||||
|
|
@ -389,6 +348,8 @@ struct f2fs_map_blocks {
|
|||
#define F2FS_GET_BLOCK_DIO 1
|
||||
#define F2FS_GET_BLOCK_FIEMAP 2
|
||||
#define F2FS_GET_BLOCK_BMAP 3
|
||||
#define F2FS_GET_BLOCK_PRE_DIO 4
|
||||
#define F2FS_GET_BLOCK_PRE_AIO 5
|
||||
|
||||
/*
|
||||
* i_advise uses FADVISE_XXX_BIT. We can add additional hints later.
|
||||
|
|
@ -410,15 +371,6 @@ struct f2fs_map_blocks {
|
|||
#define file_enc_name(inode) is_file(inode, FADVISE_ENC_NAME_BIT)
|
||||
#define file_set_enc_name(inode) set_file(inode, FADVISE_ENC_NAME_BIT)
|
||||
|
||||
/* Encryption algorithms */
|
||||
#define F2FS_ENCRYPTION_MODE_INVALID 0
|
||||
#define F2FS_ENCRYPTION_MODE_AES_256_XTS 1
|
||||
#define F2FS_ENCRYPTION_MODE_AES_256_GCM 2
|
||||
#define F2FS_ENCRYPTION_MODE_AES_256_CBC 3
|
||||
#define F2FS_ENCRYPTION_MODE_AES_256_CTS 4
|
||||
|
||||
#include "f2fs_crypto.h"
|
||||
|
||||
#define DEF_DIR_LEVEL 0
|
||||
|
||||
struct f2fs_inode_info {
|
||||
|
|
@ -442,13 +394,7 @@ struct f2fs_inode_info {
|
|||
struct list_head dirty_list; /* linked in global dirty list */
|
||||
struct list_head inmem_pages; /* inmemory pages managed by f2fs */
|
||||
struct mutex inmem_lock; /* lock for inmemory pages */
|
||||
|
||||
struct extent_tree *extent_tree; /* cached extent_tree entry */
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
/* Encryption params */
|
||||
struct f2fs_crypt_info *i_crypt_info;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline void get_extent_info(struct extent_info *ext,
|
||||
|
|
@ -515,6 +461,7 @@ struct f2fs_nm_info {
|
|||
nid_t next_scan_nid; /* the next nid to be scanned */
|
||||
unsigned int ram_thresh; /* control the memory footprint */
|
||||
unsigned int ra_nid_pages; /* # of nid pages to be readaheaded */
|
||||
unsigned int dirty_nats_ratio; /* control dirty nats ratio threshold */
|
||||
|
||||
/* NAT cache management */
|
||||
struct radix_tree_root nat_root;/* root of the nat entry cache */
|
||||
|
|
@ -549,6 +496,8 @@ struct dnode_of_data {
|
|||
unsigned int ofs_in_node; /* data offset in the node page */
|
||||
bool inode_page_locked; /* inode page is locked or not */
|
||||
bool node_changed; /* is node block changed */
|
||||
char cur_level; /* level of hole node page */
|
||||
char max_level; /* level of current page located */
|
||||
block_t data_blkaddr; /* block address of the node block */
|
||||
};
|
||||
|
||||
|
|
@ -679,6 +628,7 @@ enum page_type {
|
|||
META_FLUSH,
|
||||
INMEM, /* the below types are used by tracepoints only. */
|
||||
INMEM_DROP,
|
||||
INMEM_REVOKE,
|
||||
IPU,
|
||||
OPU,
|
||||
};
|
||||
|
|
@ -687,7 +637,8 @@ struct f2fs_io_info {
|
|||
struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */
|
||||
enum page_type type; /* contains DATA/NODE/META/META_FLUSH */
|
||||
int rw; /* contains R/RS/W/WS with REQ_META/REQ_PRIO */
|
||||
block_t blk_addr; /* block address to be written */
|
||||
block_t new_blkaddr; /* new block address to be written */
|
||||
block_t old_blkaddr; /* old block address before Cow */
|
||||
struct page *page; /* page to be written */
|
||||
struct page *encrypted_page; /* encrypted page */
|
||||
};
|
||||
|
|
@ -844,8 +795,22 @@ struct f2fs_sb_info {
|
|||
struct list_head s_list;
|
||||
struct mutex umount_mutex;
|
||||
unsigned int shrinker_run_no;
|
||||
|
||||
/* For write statistics */
|
||||
u64 sectors_written_start;
|
||||
u64 kbytes_written;
|
||||
|
||||
/* Reference to checksum algorithm driver via cryptoapi */
|
||||
struct crypto_shash *s_chksum_driver;
|
||||
};
|
||||
|
||||
/* For write statistics. Suppose sector size is 512 bytes,
|
||||
* and the return value is in kbytes. s is of struct f2fs_sb_info.
|
||||
*/
|
||||
#define BD_PART_WRITTEN(s) \
|
||||
(((u64)part_stat_read(s->sb->s_bdev->bd_part, sectors[1]) - \
|
||||
s->sectors_written_start) >> 1)
|
||||
|
||||
static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type)
|
||||
{
|
||||
sbi->last_time[type] = jiffies;
|
||||
|
|
@ -874,6 +839,29 @@ static inline bool is_idle(struct f2fs_sb_info *sbi)
|
|||
/*
|
||||
* Inline functions
|
||||
*/
|
||||
static inline u32 f2fs_crc32(struct f2fs_sb_info *sbi, const void *address,
|
||||
unsigned int length)
|
||||
{
|
||||
SHASH_DESC_ON_STACK(shash, sbi->s_chksum_driver);
|
||||
u32 *ctx = (u32 *)shash_desc_ctx(shash);
|
||||
int err;
|
||||
|
||||
shash->tfm = sbi->s_chksum_driver;
|
||||
shash->flags = 0;
|
||||
*ctx = F2FS_SUPER_MAGIC;
|
||||
|
||||
err = crypto_shash_update(shash, address, length);
|
||||
BUG_ON(err);
|
||||
|
||||
return *ctx;
|
||||
}
|
||||
|
||||
static inline bool f2fs_crc_valid(struct f2fs_sb_info *sbi, __u32 blk_crc,
|
||||
void *buf, size_t buf_size)
|
||||
{
|
||||
return f2fs_crc32(sbi, buf, buf_size) == blk_crc;
|
||||
}
|
||||
|
||||
static inline struct f2fs_inode_info *F2FS_I(struct inode *inode)
|
||||
{
|
||||
return container_of(inode, struct f2fs_inode_info, vfs_inode);
|
||||
|
|
@ -1006,7 +994,7 @@ static inline void f2fs_unlock_op(struct f2fs_sb_info *sbi)
|
|||
|
||||
static inline void f2fs_lock_all(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
f2fs_down_write(&sbi->cp_rwsem, &sbi->cp_mutex);
|
||||
down_write(&sbi->cp_rwsem);
|
||||
}
|
||||
|
||||
static inline void f2fs_unlock_all(struct f2fs_sb_info *sbi)
|
||||
|
|
@ -1525,9 +1513,9 @@ static inline int f2fs_has_inline_xattr(struct inode *inode)
|
|||
return is_inode_flag_set(F2FS_I(inode), FI_INLINE_XATTR);
|
||||
}
|
||||
|
||||
static inline unsigned int addrs_per_inode(struct f2fs_inode_info *fi)
|
||||
static inline unsigned int addrs_per_inode(struct inode *inode)
|
||||
{
|
||||
if (f2fs_has_inline_xattr(&fi->vfs_inode))
|
||||
if (f2fs_has_inline_xattr(inode))
|
||||
return DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS;
|
||||
return DEF_ADDRS_PER_INODE;
|
||||
}
|
||||
|
|
@ -1681,10 +1669,10 @@ static inline void *f2fs_kvzalloc(size_t size, gfp_t flags)
|
|||
(F2FS_I(i)->i_acl_mode) : ((i)->i_mode))
|
||||
|
||||
/* get offset of first page in next direct node */
|
||||
#define PGOFS_OF_NEXT_DNODE(pgofs, fi) \
|
||||
((pgofs < ADDRS_PER_INODE(fi)) ? ADDRS_PER_INODE(fi) : \
|
||||
(pgofs - ADDRS_PER_INODE(fi) + ADDRS_PER_BLOCK) / \
|
||||
ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi))
|
||||
#define PGOFS_OF_NEXT_DNODE(pgofs, inode) \
|
||||
((pgofs < ADDRS_PER_INODE(inode)) ? ADDRS_PER_INODE(inode) : \
|
||||
(pgofs - ADDRS_PER_INODE(inode) + ADDRS_PER_BLOCK) / \
|
||||
ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode))
|
||||
|
||||
/*
|
||||
* file.c
|
||||
|
|
@ -1723,10 +1711,10 @@ struct dentry *f2fs_get_parent(struct dentry *child);
|
|||
extern unsigned char f2fs_filetype_table[F2FS_FT_MAX];
|
||||
void set_de_type(struct f2fs_dir_entry *, umode_t);
|
||||
|
||||
struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *,
|
||||
struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *,
|
||||
f2fs_hash_t, int *, struct f2fs_dentry_ptr *);
|
||||
bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *,
|
||||
unsigned int, struct f2fs_str *);
|
||||
unsigned int, struct fscrypt_str *);
|
||||
void do_make_empty_dir(struct inode *, struct inode *,
|
||||
struct f2fs_dentry_ptr *);
|
||||
struct page *init_inode_metadata(struct inode *, struct inode *,
|
||||
|
|
@ -1763,6 +1751,7 @@ int f2fs_commit_super(struct f2fs_sb_info *, bool);
|
|||
int f2fs_sync_fs(struct super_block *, int);
|
||||
extern __printf(3, 4)
|
||||
void f2fs_msg(struct super_block *, const char *, const char *, ...);
|
||||
int sanity_check_ckpt(struct f2fs_sb_info *sbi);
|
||||
|
||||
/*
|
||||
* hash.c
|
||||
|
|
@ -1780,6 +1769,7 @@ int need_dentry_mark(struct f2fs_sb_info *, nid_t);
|
|||
bool is_checkpointed_node(struct f2fs_sb_info *, nid_t);
|
||||
bool need_inode_block_update(struct f2fs_sb_info *, nid_t);
|
||||
void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *);
|
||||
pgoff_t get_next_page_offset(struct dnode_of_data *, pgoff_t);
|
||||
int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int);
|
||||
int truncate_inode_blocks(struct inode *, pgoff_t);
|
||||
int truncate_xattr_node(struct inode *, struct page *);
|
||||
|
|
@ -1811,7 +1801,8 @@ void destroy_node_manager_caches(void);
|
|||
* segment.c
|
||||
*/
|
||||
void register_inmem_page(struct inode *, struct page *);
|
||||
int commit_inmem_pages(struct inode *, bool);
|
||||
void drop_inmem_pages(struct inode *);
|
||||
int commit_inmem_pages(struct inode *);
|
||||
void f2fs_balance_fs(struct f2fs_sb_info *, bool);
|
||||
void f2fs_balance_fs_bg(struct f2fs_sb_info *);
|
||||
int f2fs_issue_flush(struct f2fs_sb_info *);
|
||||
|
|
@ -1832,16 +1823,17 @@ void write_meta_page(struct f2fs_sb_info *, struct page *);
|
|||
void write_node_page(unsigned int, struct f2fs_io_info *);
|
||||
void write_data_page(struct dnode_of_data *, struct f2fs_io_info *);
|
||||
void rewrite_data_page(struct f2fs_io_info *);
|
||||
void __f2fs_replace_block(struct f2fs_sb_info *, struct f2fs_summary *,
|
||||
block_t, block_t, bool, bool);
|
||||
void f2fs_replace_block(struct f2fs_sb_info *, struct dnode_of_data *,
|
||||
block_t, block_t, unsigned char, bool);
|
||||
block_t, block_t, unsigned char, bool, bool);
|
||||
void allocate_data_block(struct f2fs_sb_info *, struct page *,
|
||||
block_t, block_t *, struct f2fs_summary *, int);
|
||||
void f2fs_wait_on_page_writeback(struct page *, enum page_type);
|
||||
void f2fs_wait_on_page_writeback(struct page *, enum page_type, bool);
|
||||
void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *, block_t);
|
||||
void write_data_summaries(struct f2fs_sb_info *, block_t);
|
||||
void write_node_summaries(struct f2fs_sb_info *, block_t);
|
||||
int lookup_journal_in_cursum(struct f2fs_summary_block *,
|
||||
int, unsigned int, int);
|
||||
int lookup_journal_in_cursum(struct f2fs_journal *, int, unsigned int, int);
|
||||
void flush_sit_entries(struct f2fs_sb_info *, struct cp_control *);
|
||||
int build_segment_manager(struct f2fs_sb_info *);
|
||||
void destroy_segment_manager(struct f2fs_sb_info *);
|
||||
|
|
@ -1881,11 +1873,16 @@ void destroy_checkpoint_caches(void);
|
|||
* data.c
|
||||
*/
|
||||
void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int);
|
||||
void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *, struct inode *,
|
||||
struct page *, nid_t, enum page_type, int);
|
||||
void f2fs_flush_merged_bios(struct f2fs_sb_info *);
|
||||
int f2fs_submit_page_bio(struct f2fs_io_info *);
|
||||
void f2fs_submit_page_mbio(struct f2fs_io_info *);
|
||||
void set_data_blkaddr(struct dnode_of_data *);
|
||||
void f2fs_update_data_blkaddr(struct dnode_of_data *, block_t);
|
||||
int reserve_new_block(struct dnode_of_data *);
|
||||
int f2fs_get_block(struct dnode_of_data *, pgoff_t);
|
||||
ssize_t f2fs_preallocate_blocks(struct kiocb *, struct iov_iter *);
|
||||
int f2fs_reserve_block(struct dnode_of_data *, pgoff_t);
|
||||
struct page *get_read_data_page(struct inode *, pgoff_t, int, bool);
|
||||
struct page *find_data_page(struct inode *, pgoff_t);
|
||||
|
|
@ -1902,7 +1899,7 @@ int f2fs_release_page(struct page *, gfp_t);
|
|||
*/
|
||||
int start_gc_thread(struct f2fs_sb_info *);
|
||||
void stop_gc_thread(struct f2fs_sb_info *);
|
||||
block_t start_bidx_of_node(unsigned int, struct f2fs_inode_info *);
|
||||
block_t start_bidx_of_node(unsigned int, struct inode *);
|
||||
int f2fs_gc(struct f2fs_sb_info *, bool);
|
||||
void build_gc_manager(struct f2fs_sb_info *);
|
||||
|
||||
|
|
@ -2093,7 +2090,7 @@ int f2fs_convert_inline_inode(struct inode *);
|
|||
int f2fs_write_inline_data(struct inode *, struct page *);
|
||||
bool recover_inline_data(struct inode *, struct page *);
|
||||
struct f2fs_dir_entry *find_in_inline_dir(struct inode *,
|
||||
struct f2fs_filename *, struct page **);
|
||||
struct fscrypt_name *, struct page **);
|
||||
struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **);
|
||||
int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *);
|
||||
int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *,
|
||||
|
|
@ -2102,7 +2099,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *,
|
|||
struct inode *, struct inode *);
|
||||
bool f2fs_empty_inline_dir(struct inode *);
|
||||
int f2fs_read_inline_dir(struct file *, struct dir_context *,
|
||||
struct f2fs_str *);
|
||||
struct fscrypt_str *);
|
||||
int f2fs_inline_data_fiemap(struct inode *,
|
||||
struct fiemap_extent_info *, __u64, __u64);
|
||||
|
||||
|
|
@ -2132,13 +2129,9 @@ void destroy_extent_cache(void);
|
|||
/*
|
||||
* crypto support
|
||||
*/
|
||||
static inline int f2fs_encrypted_inode(struct inode *inode)
|
||||
static inline bool f2fs_encrypted_inode(struct inode *inode)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
return file_is_encrypt(inode);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void f2fs_set_encrypted_inode(struct inode *inode)
|
||||
|
|
@ -2150,20 +2143,12 @@ static inline void f2fs_set_encrypted_inode(struct inode *inode)
|
|||
|
||||
static inline bool f2fs_bio_encrypted(struct bio *bio)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
return unlikely(bio->bi_private != NULL);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
return bio->bi_private != NULL;
|
||||
}
|
||||
|
||||
static inline int f2fs_sb_has_crypto(struct super_block *sb)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool f2fs_may_encrypt(struct inode *inode)
|
||||
|
|
@ -2177,86 +2162,28 @@ static inline bool f2fs_may_encrypt(struct inode *inode)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* crypto_policy.c */
|
||||
int f2fs_is_child_context_consistent_with_parent(struct inode *,
|
||||
struct inode *);
|
||||
int f2fs_inherit_context(struct inode *, struct inode *, struct page *);
|
||||
int f2fs_process_policy(const struct f2fs_encryption_policy *, struct inode *);
|
||||
int f2fs_get_policy(struct inode *, struct f2fs_encryption_policy *);
|
||||
|
||||
/* crypt.c */
|
||||
extern struct kmem_cache *f2fs_crypt_info_cachep;
|
||||
bool f2fs_valid_contents_enc_mode(uint32_t);
|
||||
uint32_t f2fs_validate_encryption_key_size(uint32_t, uint32_t);
|
||||
struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *);
|
||||
void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *);
|
||||
struct page *f2fs_encrypt(struct inode *, struct page *);
|
||||
int f2fs_decrypt(struct f2fs_crypto_ctx *, struct page *);
|
||||
int f2fs_decrypt_one(struct inode *, struct page *);
|
||||
void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *, struct bio *);
|
||||
|
||||
/* crypto_key.c */
|
||||
void f2fs_free_encryption_info(struct inode *, struct f2fs_crypt_info *);
|
||||
int _f2fs_get_encryption_info(struct inode *inode);
|
||||
|
||||
/* crypto_fname.c */
|
||||
bool f2fs_valid_filenames_enc_mode(uint32_t);
|
||||
u32 f2fs_fname_crypto_round_up(u32, u32);
|
||||
int f2fs_fname_crypto_alloc_buffer(struct inode *, u32, struct f2fs_str *);
|
||||
int f2fs_fname_disk_to_usr(struct inode *, f2fs_hash_t *,
|
||||
const struct f2fs_str *, struct f2fs_str *);
|
||||
int f2fs_fname_usr_to_disk(struct inode *, const struct qstr *,
|
||||
struct f2fs_str *);
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
void f2fs_restore_and_release_control_page(struct page **);
|
||||
void f2fs_restore_control_page(struct page *);
|
||||
|
||||
int __init f2fs_init_crypto(void);
|
||||
int f2fs_crypto_initialize(void);
|
||||
void f2fs_exit_crypto(void);
|
||||
|
||||
int f2fs_has_encryption_key(struct inode *);
|
||||
|
||||
static inline int f2fs_get_encryption_info(struct inode *inode)
|
||||
{
|
||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
||||
|
||||
if (!ci ||
|
||||
(ci->ci_keyring_key &&
|
||||
(ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
|
||||
(1 << KEY_FLAG_REVOKED) |
|
||||
(1 << KEY_FLAG_DEAD)))))
|
||||
return _f2fs_get_encryption_info(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f2fs_fname_crypto_free_buffer(struct f2fs_str *);
|
||||
int f2fs_fname_setup_filename(struct inode *, const struct qstr *,
|
||||
int lookup, struct f2fs_filename *);
|
||||
void f2fs_fname_free_filename(struct f2fs_filename *);
|
||||
#else
|
||||
static inline void f2fs_restore_and_release_control_page(struct page **p) { }
|
||||
static inline void f2fs_restore_control_page(struct page *p) { }
|
||||
|
||||
static inline int __init f2fs_init_crypto(void) { return 0; }
|
||||
static inline void f2fs_exit_crypto(void) { }
|
||||
|
||||
static inline int f2fs_has_encryption_key(struct inode *i) { return 0; }
|
||||
static inline int f2fs_get_encryption_info(struct inode *i) { return 0; }
|
||||
static inline void f2fs_fname_crypto_free_buffer(struct f2fs_str *p) { }
|
||||
|
||||
static inline int f2fs_fname_setup_filename(struct inode *dir,
|
||||
const struct qstr *iname,
|
||||
int lookup, struct f2fs_filename *fname)
|
||||
{
|
||||
memset(fname, 0, sizeof(struct f2fs_filename));
|
||||
fname->usr_fname = iname;
|
||||
fname->disk_name.name = (unsigned char *)iname->name;
|
||||
fname->disk_name.len = iname->len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void f2fs_fname_free_filename(struct f2fs_filename *fname) { }
|
||||
#ifndef CONFIG_F2FS_FS_ENCRYPTION
|
||||
#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
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* linux/fs/f2fs/f2fs_crypto.h
|
||||
*
|
||||
* Copied from linux/fs/ext4/ext4_crypto.h
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* This contains encryption header content for f2fs
|
||||
*
|
||||
* Written by Michael Halcrow, 2015.
|
||||
* Modified by Jaegeuk Kim, 2015.
|
||||
*/
|
||||
#ifndef _F2FS_CRYPTO_H
|
||||
#define _F2FS_CRYPTO_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
#define F2FS_KEY_DESCRIPTOR_SIZE 8
|
||||
|
||||
/* Policy provided via an ioctl on the topmost directory */
|
||||
struct f2fs_encryption_policy {
|
||||
char version;
|
||||
char contents_encryption_mode;
|
||||
char filenames_encryption_mode;
|
||||
char flags;
|
||||
char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define F2FS_ENCRYPTION_CONTEXT_FORMAT_V1 1
|
||||
#define F2FS_KEY_DERIVATION_NONCE_SIZE 16
|
||||
|
||||
#define F2FS_POLICY_FLAGS_PAD_4 0x00
|
||||
#define F2FS_POLICY_FLAGS_PAD_8 0x01
|
||||
#define F2FS_POLICY_FLAGS_PAD_16 0x02
|
||||
#define F2FS_POLICY_FLAGS_PAD_32 0x03
|
||||
#define F2FS_POLICY_FLAGS_PAD_MASK 0x03
|
||||
#define F2FS_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: Flags
|
||||
* 8 bytes: Master Key descriptor
|
||||
* 16 bytes: Encryption Key derivation nonce
|
||||
*/
|
||||
struct f2fs_encryption_context {
|
||||
char format;
|
||||
char contents_encryption_mode;
|
||||
char filenames_encryption_mode;
|
||||
char flags;
|
||||
char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE];
|
||||
char nonce[F2FS_KEY_DERIVATION_NONCE_SIZE];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* Encryption parameters */
|
||||
#define F2FS_XTS_TWEAK_SIZE 16
|
||||
#define F2FS_AES_128_ECB_KEY_SIZE 16
|
||||
#define F2FS_AES_256_GCM_KEY_SIZE 32
|
||||
#define F2FS_AES_256_CBC_KEY_SIZE 32
|
||||
#define F2FS_AES_256_CTS_KEY_SIZE 32
|
||||
#define F2FS_AES_256_XTS_KEY_SIZE 64
|
||||
#define F2FS_MAX_KEY_SIZE 64
|
||||
|
||||
#define F2FS_KEY_DESC_PREFIX "f2fs:"
|
||||
#define F2FS_KEY_DESC_PREFIX_SIZE 5
|
||||
|
||||
struct f2fs_encryption_key {
|
||||
__u32 mode;
|
||||
char raw[F2FS_MAX_KEY_SIZE];
|
||||
__u32 size;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct f2fs_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[F2FS_KEY_DESCRIPTOR_SIZE];
|
||||
};
|
||||
|
||||
#define F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
|
||||
#define F2FS_WRITE_PATH_FL 0x00000002
|
||||
|
||||
struct f2fs_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 */
|
||||
};
|
||||
|
||||
struct f2fs_completion_result {
|
||||
struct completion completion;
|
||||
int res;
|
||||
};
|
||||
|
||||
#define DECLARE_F2FS_COMPLETION_RESULT(ecr) \
|
||||
struct f2fs_completion_result ecr = { \
|
||||
COMPLETION_INITIALIZER((ecr).completion), 0 }
|
||||
|
||||
static inline int f2fs_encryption_key_size(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case F2FS_ENCRYPTION_MODE_AES_256_XTS:
|
||||
return F2FS_AES_256_XTS_KEY_SIZE;
|
||||
case F2FS_ENCRYPTION_MODE_AES_256_GCM:
|
||||
return F2FS_AES_256_GCM_KEY_SIZE;
|
||||
case F2FS_ENCRYPTION_MODE_AES_256_CBC:
|
||||
return F2FS_AES_256_CBC_KEY_SIZE;
|
||||
case F2FS_ENCRYPTION_MODE_AES_256_CTS:
|
||||
return F2FS_AES_256_CTS_KEY_SIZE;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define F2FS_FNAME_NUM_SCATTER_ENTRIES 4
|
||||
#define F2FS_CRYPTO_BLOCK_SIZE 16
|
||||
#define F2FS_FNAME_CRYPTO_DIGEST_SIZE 32
|
||||
|
||||
/**
|
||||
* For encrypted symlinks, the ciphertext length is stored at the beginning
|
||||
* of the string in little-endian format.
|
||||
*/
|
||||
struct f2fs_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)
|
||||
{
|
||||
return (l + sizeof(struct f2fs_encrypted_symlink_data) - 1);
|
||||
}
|
||||
#endif /* _F2FS_CRYPTO_H */
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue