mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-04 05:46:49 +00:00
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
@ -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");
|
@ -1,44 +1,32 @@
|
||||
/*
|
||||
* linux/fs/f2fs/crypto_fname.c
|
||||
*
|
||||
* Copied from linux/fs/ext4/crypto.c
|
||||
* This contains functions for filename crypto management
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
* Copyright (C) 2015, Motorola Mobility
|
||||
*
|
||||
* This contains functions for filename crypto management in f2fs
|
||||
*
|
||||
* Written by Uday Savagaonkar, 2014.
|
||||
*
|
||||
* Adjust f2fs dentry structure
|
||||
* Jaegeuk Kim, 2015.
|
||||
* Modified by Jaegeuk Kim, 2015.
|
||||
*
|
||||
* This has not yet undergone a rigorous security audit.
|
||||
*/
|
||||
#include <crypto/skcipher.h>
|
||||
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mempool.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/fscrypto.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "f2fs_crypto.h"
|
||||
#include "xattr.h"
|
||||
static u32 size_round_up(size_t size, size_t blksize)
|
||||
{
|
||||
return ((size + blksize - 1) / blksize) * blksize;
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_dir_crypt_complete() -
|
||||
* dir_crypt_complete() -
|
||||
*/
|
||||
static void f2fs_dir_crypt_complete(struct crypto_async_request *req, int res)
|
||||
static void dir_crypt_complete(struct crypto_async_request *req, int res)
|
||||
{
|
||||
struct f2fs_completion_result *ecr = req->data;
|
||||
struct fscrypt_completion_result *ecr = req->data;
|
||||
|
||||
if (res == -EINPROGRESS)
|
||||
return;
|
||||
@ -46,45 +34,35 @@ static void f2fs_dir_crypt_complete(struct crypto_async_request *req, int res)
|
||||
complete(&ecr->completion);
|
||||
}
|
||||
|
||||
bool f2fs_valid_filenames_enc_mode(uint32_t mode)
|
||||
{
|
||||
return (mode == F2FS_ENCRYPTION_MODE_AES_256_CTS);
|
||||
}
|
||||
|
||||
static unsigned max_name_len(struct inode *inode)
|
||||
{
|
||||
return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize :
|
||||
F2FS_NAME_LEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_fname_encrypt() -
|
||||
* 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 f2fs_fname_encrypt(struct inode *inode,
|
||||
const struct qstr *iname, struct f2fs_str *oname)
|
||||
static int fname_encrypt(struct inode *inode,
|
||||
const struct qstr *iname, struct fscrypt_str *oname)
|
||||
{
|
||||
u32 ciphertext_len;
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_F2FS_COMPLETION_RESULT(ecr);
|
||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
||||
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[F2FS_CRYPTO_BLOCK_SIZE];
|
||||
char iv[FS_CRYPTO_BLOCK_SIZE];
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
int padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK);
|
||||
int padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK);
|
||||
char *workbuf, buf[32], *alloc_buf = NULL;
|
||||
unsigned lim = max_name_len(inode);
|
||||
unsigned lim;
|
||||
|
||||
lim = inode->i_sb->s_cop->max_namelen(inode);
|
||||
if (iname->len <= 0 || iname->len > lim)
|
||||
return -EIO;
|
||||
|
||||
ciphertext_len = (iname->len < F2FS_CRYPTO_BLOCK_SIZE) ?
|
||||
F2FS_CRYPTO_BLOCK_SIZE : iname->len;
|
||||
ciphertext_len = f2fs_fname_crypto_round_up(ciphertext_len, padding);
|
||||
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)) {
|
||||
@ -106,7 +84,7 @@ static int f2fs_fname_encrypt(struct inode *inode,
|
||||
}
|
||||
skcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
f2fs_dir_crypt_complete, &ecr);
|
||||
dir_crypt_complete, &ecr);
|
||||
|
||||
/* Copy the input */
|
||||
memcpy(workbuf, iname->name, iname->len);
|
||||
@ -114,7 +92,7 @@ static int f2fs_fname_encrypt(struct inode *inode,
|
||||
memset(workbuf + iname->len, 0, ciphertext_len - iname->len);
|
||||
|
||||
/* Initialize IV */
|
||||
memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE);
|
||||
memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
|
||||
|
||||
/* Create encryption request */
|
||||
sg_init_one(&src_sg, workbuf, ciphertext_len);
|
||||
@ -122,39 +100,40 @@ static int f2fs_fname_encrypt(struct inode *inode,
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv);
|
||||
res = crypto_skcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
BUG_ON(req->base.data != &ecr);
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
kfree(alloc_buf);
|
||||
skcipher_request_free(req);
|
||||
if (res < 0) {
|
||||
if (res < 0)
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: Error (error code %d)\n", __func__, res);
|
||||
}
|
||||
|
||||
oname->len = ciphertext_len;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* f2fs_fname_decrypt()
|
||||
* 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 f2fs_fname_decrypt(struct inode *inode,
|
||||
const struct f2fs_str *iname, struct f2fs_str *oname)
|
||||
static int fname_decrypt(struct inode *inode,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
struct skcipher_request *req = NULL;
|
||||
DECLARE_F2FS_COMPLETION_RESULT(ecr);
|
||||
DECLARE_FS_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
||||
struct fscrypt_info *ci = inode->i_crypt_info;
|
||||
struct crypto_skcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
char iv[F2FS_CRYPTO_BLOCK_SIZE];
|
||||
unsigned lim = max_name_len(inode);
|
||||
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;
|
||||
|
||||
@ -167,10 +146,10 @@ static int f2fs_fname_decrypt(struct inode *inode,
|
||||
}
|
||||
skcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
f2fs_dir_crypt_complete, &ecr);
|
||||
dir_crypt_complete, &ecr);
|
||||
|
||||
/* Initialize IV */
|
||||
memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE);
|
||||
memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
|
||||
|
||||
/* Create decryption request */
|
||||
sg_init_one(&src_sg, iname->name, iname->len);
|
||||
@ -178,15 +157,13 @@ static int f2fs_fname_decrypt(struct inode *inode,
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv);
|
||||
res = crypto_skcipher_decrypt(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 < 0) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: Error in f2fs_fname_decrypt (error code %d)\n",
|
||||
__func__, res);
|
||||
"%s: Error (error code %d)\n", __func__, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -198,7 +175,7 @@ static const char *lookup_table =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
||||
|
||||
/**
|
||||
* f2fs_fname_encode_digest() -
|
||||
* 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.
|
||||
@ -247,148 +224,152 @@ static int digest_decode(const char *src, int len, char *dst)
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_fname_crypto_round_up() -
|
||||
*
|
||||
* Return: The next multiple of block size
|
||||
*/
|
||||
u32 f2fs_fname_crypto_round_up(u32 size, u32 blksize)
|
||||
u32 fscrypt_fname_encrypted_size(struct inode *inode, u32 ilen)
|
||||
{
|
||||
return ((size + blksize - 1) / blksize) * blksize;
|
||||
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);
|
||||
|
||||
/**
|
||||
* f2fs_fname_crypto_alloc_obuff() -
|
||||
* fscrypt_fname_crypto_alloc_obuff() -
|
||||
*
|
||||
* Allocates an output buffer that is sufficient for the crypto operation
|
||||
* specified by the context and the direction.
|
||||
*/
|
||||
int f2fs_fname_crypto_alloc_buffer(struct inode *inode,
|
||||
u32 ilen, struct f2fs_str *crypto_str)
|
||||
int fscrypt_fname_alloc_buffer(struct inode *inode,
|
||||
u32 ilen, struct fscrypt_str *crypto_str)
|
||||
{
|
||||
unsigned int olen;
|
||||
int padding = 16;
|
||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
||||
unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen);
|
||||
|
||||
if (ci)
|
||||
padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK);
|
||||
if (padding < F2FS_CRYPTO_BLOCK_SIZE)
|
||||
padding = F2FS_CRYPTO_BLOCK_SIZE;
|
||||
olen = f2fs_fname_crypto_round_up(ilen, padding);
|
||||
crypto_str->len = olen;
|
||||
if (olen < F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
|
||||
olen = F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
|
||||
/* Allocated buffer can hold one more character to null-terminate the
|
||||
* string */
|
||||
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);
|
||||
|
||||
/**
|
||||
* f2fs_fname_crypto_free_buffer() -
|
||||
* fscrypt_fname_crypto_free_buffer() -
|
||||
*
|
||||
* Frees the buffer allocated for crypto operation.
|
||||
*/
|
||||
void f2fs_fname_crypto_free_buffer(struct f2fs_str *crypto_str)
|
||||
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);
|
||||
|
||||
/**
|
||||
* f2fs_fname_disk_to_usr() - converts a filename from disk space to user space
|
||||
* fscrypt_fname_disk_to_usr() - converts a filename from disk space to user
|
||||
* space
|
||||
*/
|
||||
int f2fs_fname_disk_to_usr(struct inode *inode,
|
||||
f2fs_hash_t *hash,
|
||||
const struct f2fs_str *iname,
|
||||
struct f2fs_str *oname)
|
||||
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 (is_dot_dotdot(&qname)) {
|
||||
if (fscrypt_is_dot_dotdot(&qname)) {
|
||||
oname->name[0] = '.';
|
||||
oname->name[iname->len - 1] = '.';
|
||||
oname->len = iname->len;
|
||||
return oname->len;
|
||||
}
|
||||
|
||||
if (F2FS_I(inode)->i_crypt_info)
|
||||
return f2fs_fname_decrypt(inode, iname, oname);
|
||||
if (iname->len < FS_CRYPTO_BLOCK_SIZE)
|
||||
return -EUCLEAN;
|
||||
|
||||
if (iname->len <= F2FS_FNAME_CRYPTO_DIGEST_SIZE) {
|
||||
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);
|
||||
memset(buf + 4, 0, 4);
|
||||
} else
|
||||
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);
|
||||
|
||||
/**
|
||||
* f2fs_fname_usr_to_disk() - converts a filename from user space to disk space
|
||||
* fscrypt_fname_usr_to_disk() - converts a filename from user space to disk
|
||||
* space
|
||||
*/
|
||||
int f2fs_fname_usr_to_disk(struct inode *inode,
|
||||
int fscrypt_fname_usr_to_disk(struct inode *inode,
|
||||
const struct qstr *iname,
|
||||
struct f2fs_str *oname)
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
int res;
|
||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
||||
|
||||
if (is_dot_dotdot(iname)) {
|
||||
if (fscrypt_is_dot_dotdot(iname)) {
|
||||
oname->name[0] = '.';
|
||||
oname->name[iname->len - 1] = '.';
|
||||
oname->len = iname->len;
|
||||
return oname->len;
|
||||
}
|
||||
|
||||
if (ci) {
|
||||
res = f2fs_fname_encrypt(inode, iname, oname);
|
||||
return res;
|
||||
}
|
||||
/* Without a proper key, a user is not allowed to modify the filenames
|
||||
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 */
|
||||
* a disk-space name
|
||||
*/
|
||||
return -EACCES;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_fname_usr_to_disk);
|
||||
|
||||
int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct f2fs_filename *fname)
|
||||
int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct fscrypt_name *fname)
|
||||
{
|
||||
struct f2fs_crypt_info *ci;
|
||||
int ret = 0, bigname = 0;
|
||||
|
||||
memset(fname, 0, sizeof(struct f2fs_filename));
|
||||
memset(fname, 0, sizeof(struct fscrypt_name));
|
||||
fname->usr_fname = iname;
|
||||
|
||||
if (!f2fs_encrypted_inode(dir) || is_dot_dotdot(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 = f2fs_get_encryption_info(dir);
|
||||
if (ret)
|
||||
ret = get_crypt_info(dir);
|
||||
if (ret && ret != -EOPNOTSUPP)
|
||||
return ret;
|
||||
ci = F2FS_I(dir)->i_crypt_info;
|
||||
if (ci) {
|
||||
ret = f2fs_fname_crypto_alloc_buffer(dir, iname->len,
|
||||
&fname->crypto_buf);
|
||||
|
||||
if (dir->i_crypt_info) {
|
||||
ret = fscrypt_fname_alloc_buffer(dir, iname->len,
|
||||
&fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = f2fs_fname_encrypt(dir, iname, &fname->crypto_buf);
|
||||
ret = fname_encrypt(dir, iname, &fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
goto errout;
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
@ -398,18 +379,19 @@ int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
if (!lookup)
|
||||
return -EACCES;
|
||||
|
||||
/* We don't have the key and we are doing a lookup; decode the
|
||||
/*
|
||||
* 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)))
|
||||
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) {
|
||||
@ -419,20 +401,24 @@ int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
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:
|
||||
f2fs_fname_crypto_free_buffer(&fname->crypto_buf);
|
||||
fscrypt_fname_free_buffer(&fname->crypto_buf);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fscrypt_setup_filename);
|
||||
|
||||
void f2fs_fname_free_filename(struct f2fs_filename *fname)
|
||||
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 */
|
114
fs/f2fs/file.c
114
fs/f2fs/file.c
@ -86,7 +86,7 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
|
||||
trace_f2fs_vm_page_mkwrite(page, DATA);
|
||||
mapped:
|
||||
/* fill the page */
|
||||
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))
|
||||
@ -301,7 +301,7 @@ static pgoff_t __get_first_dirty_index(struct address_space *mapping,
|
||||
pagevec_init(&pvec, 0);
|
||||
nr_pages = pagevec_lookup_tag(&pvec, mapping, &pgofs,
|
||||
PAGECACHE_TAG_DIRTY, 1);
|
||||
pgofs = nr_pages ? pvec.pages[0]->index : LONG_MAX;
|
||||
pgofs = nr_pages ? pvec.pages[0]->index : ULONG_MAX;
|
||||
pagevec_release(&pvec);
|
||||
return pgofs;
|
||||
}
|
||||
@ -358,15 +358,14 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
|
||||
} else if (err == -ENOENT) {
|
||||
/* direct node does not exists */
|
||||
if (whence == SEEK_DATA) {
|
||||
pgofs = PGOFS_OF_NEXT_DNODE(pgofs,
|
||||
F2FS_I(inode));
|
||||
pgofs = get_next_page_offset(&dn, pgofs);
|
||||
continue;
|
||||
} else {
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
|
||||
|
||||
/* find data/hole in dnode block */
|
||||
for (; dn.ofs_in_node < end_offset;
|
||||
@ -422,9 +421,11 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
int err;
|
||||
|
||||
if (f2fs_encrypted_inode(inode)) {
|
||||
err = f2fs_get_encryption_info(inode);
|
||||
err = fscrypt_get_encryption_info(inode);
|
||||
if (err)
|
||||
return 0;
|
||||
if (!f2fs_encrypted_inode(inode))
|
||||
return -ENOKEY;
|
||||
}
|
||||
|
||||
/* we don't need to use inline_data strictly */
|
||||
@ -440,12 +441,18 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
static int f2fs_file_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
int ret = generic_file_open(inode, filp);
|
||||
struct inode *dir = filp->f_path.dentry->d_parent->d_inode;
|
||||
|
||||
if (!ret && f2fs_encrypted_inode(inode)) {
|
||||
ret = f2fs_get_encryption_info(inode);
|
||||
ret = fscrypt_get_encryption_info(inode);
|
||||
if (ret)
|
||||
ret = -EACCES;
|
||||
return -EACCES;
|
||||
if (!fscrypt_has_encryption_key(inode))
|
||||
return -ENOKEY;
|
||||
}
|
||||
if (f2fs_encrypted_inode(dir) &&
|
||||
!fscrypt_has_permitted_context(dir, inode))
|
||||
return -EPERM;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -480,7 +487,7 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
|
||||
* we will invalidate all blkaddr in the whole range.
|
||||
*/
|
||||
fofs = start_bidx_of_node(ofs_of_node(dn->node_page),
|
||||
F2FS_I(dn->inode)) + ofs;
|
||||
dn->inode) + ofs;
|
||||
f2fs_update_extent_cache_range(dn, fofs, 0, len);
|
||||
dec_valid_block_count(sbi, dn->inode, nr_free);
|
||||
sync_inode_page(dn);
|
||||
@ -521,9 +528,10 @@ static int truncate_partial_data_page(struct inode *inode, u64 from,
|
||||
if (IS_ERR(page))
|
||||
return 0;
|
||||
truncate_out:
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
zero_user(page, offset, PAGE_CACHE_SIZE - offset);
|
||||
if (!cache_only || !f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode))
|
||||
if (!cache_only || !f2fs_encrypted_inode(inode) ||
|
||||
!S_ISREG(inode->i_mode))
|
||||
set_page_dirty(page);
|
||||
f2fs_put_page(page, 1);
|
||||
return 0;
|
||||
@ -568,7 +576,7 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
|
||||
goto out;
|
||||
}
|
||||
|
||||
count = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
|
||||
count = ADDRS_PER_PAGE(dn.node_page, inode);
|
||||
|
||||
count -= dn.ofs_in_node;
|
||||
f2fs_bug_on(sbi, count < 0);
|
||||
@ -671,7 +679,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
if (f2fs_encrypted_inode(inode) &&
|
||||
f2fs_get_encryption_info(inode))
|
||||
fscrypt_get_encryption_info(inode))
|
||||
return -EACCES;
|
||||
|
||||
if (attr->ia_size <= i_size_read(inode)) {
|
||||
@ -743,7 +751,7 @@ static int fill_zero(struct inode *inode, pgoff_t index,
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
zero_user(page, start, len);
|
||||
set_page_dirty(page);
|
||||
f2fs_put_page(page, 1);
|
||||
@ -768,7 +776,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end)
|
||||
return err;
|
||||
}
|
||||
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
|
||||
count = min(end_offset - dn.ofs_in_node, pg_end - pg_start);
|
||||
|
||||
f2fs_bug_on(F2FS_I_SB(inode), count == 0 || count > end_offset);
|
||||
@ -854,10 +862,8 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src,
|
||||
} else {
|
||||
new_addr = dn.data_blkaddr;
|
||||
if (!is_checkpointed_data(sbi, new_addr)) {
|
||||
dn.data_blkaddr = NULL_ADDR;
|
||||
/* do not invalidate this block address */
|
||||
set_data_blkaddr(&dn);
|
||||
f2fs_update_extent_cache(&dn);
|
||||
f2fs_update_data_blkaddr(&dn, NULL_ADDR);
|
||||
do_replace = true;
|
||||
}
|
||||
f2fs_put_dnode(&dn);
|
||||
@ -884,7 +890,7 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src,
|
||||
|
||||
get_node_info(sbi, dn.nid, &ni);
|
||||
f2fs_replace_block(sbi, &dn, dn.data_blkaddr, new_addr,
|
||||
ni.version, true);
|
||||
ni.version, true, false);
|
||||
f2fs_put_dnode(&dn);
|
||||
} else {
|
||||
struct page *psrc, *pdst;
|
||||
@ -892,7 +898,7 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src,
|
||||
psrc = get_lock_data_page(inode, src, true);
|
||||
if (IS_ERR(psrc))
|
||||
return PTR_ERR(psrc);
|
||||
pdst = get_new_data_page(inode, NULL, dst, false);
|
||||
pdst = get_new_data_page(inode, NULL, dst, true);
|
||||
if (IS_ERR(pdst)) {
|
||||
f2fs_put_page(psrc, 1);
|
||||
return PTR_ERR(pdst);
|
||||
@ -908,9 +914,7 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src,
|
||||
|
||||
err_out:
|
||||
if (!get_dnode_of_data(&dn, src, LOOKUP_NODE)) {
|
||||
dn.data_blkaddr = new_addr;
|
||||
set_data_blkaddr(&dn);
|
||||
f2fs_update_extent_cache(&dn);
|
||||
f2fs_update_data_blkaddr(&dn, new_addr);
|
||||
f2fs_put_dnode(&dn);
|
||||
}
|
||||
return ret;
|
||||
@ -1050,12 +1054,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len,
|
||||
|
||||
if (dn.data_blkaddr != NEW_ADDR) {
|
||||
invalidate_blocks(sbi, dn.data_blkaddr);
|
||||
|
||||
dn.data_blkaddr = NEW_ADDR;
|
||||
set_data_blkaddr(&dn);
|
||||
|
||||
dn.data_blkaddr = NULL_ADDR;
|
||||
f2fs_update_extent_cache(&dn);
|
||||
f2fs_update_data_blkaddr(&dn, NEW_ADDR);
|
||||
}
|
||||
f2fs_put_dnode(&dn);
|
||||
f2fs_unlock_op(sbi);
|
||||
@ -1253,7 +1252,7 @@ static int f2fs_release_file(struct inode *inode, struct file *filp)
|
||||
{
|
||||
/* some remained atomic pages should discarded */
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
commit_inmem_pages(inode, true);
|
||||
drop_inmem_pages(inode);
|
||||
if (f2fs_is_volatile_file(inode)) {
|
||||
set_inode_flag(F2FS_I(inode), FI_DROP_CACHE);
|
||||
filemap_fdatawrite(inode->i_mapping);
|
||||
@ -1377,7 +1376,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp)
|
||||
|
||||
if (f2fs_is_atomic_file(inode)) {
|
||||
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
|
||||
ret = commit_inmem_pages(inode, false);
|
||||
ret = commit_inmem_pages(inode);
|
||||
if (ret) {
|
||||
set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
|
||||
goto err_out;
|
||||
@ -1440,7 +1439,7 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp)
|
||||
|
||||
if (f2fs_is_atomic_file(inode)) {
|
||||
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
|
||||
commit_inmem_pages(inode, true);
|
||||
drop_inmem_pages(inode);
|
||||
}
|
||||
if (f2fs_is_volatile_file(inode)) {
|
||||
clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
|
||||
@ -1535,39 +1534,30 @@ static bool uuid_is_nonzero(__u8 u[16])
|
||||
|
||||
static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
struct f2fs_encryption_policy policy;
|
||||
struct fscrypt_policy policy;
|
||||
struct inode *inode = file_inode(filp);
|
||||
|
||||
if (copy_from_user(&policy, (struct f2fs_encryption_policy __user *)arg,
|
||||
sizeof(policy)))
|
||||
if (copy_from_user(&policy, (struct fscrypt_policy __user *)arg,
|
||||
sizeof(policy)))
|
||||
return -EFAULT;
|
||||
|
||||
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
|
||||
return f2fs_process_policy(&policy, inode);
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
return fscrypt_process_policy(inode, &policy);
|
||||
}
|
||||
|
||||
static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
struct f2fs_encryption_policy policy;
|
||||
struct fscrypt_policy policy;
|
||||
struct inode *inode = file_inode(filp);
|
||||
int err;
|
||||
|
||||
err = f2fs_get_policy(inode, &policy);
|
||||
err = fscrypt_get_policy(inode, &policy);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (copy_to_user((struct f2fs_encryption_policy __user *)arg, &policy,
|
||||
sizeof(policy)))
|
||||
if (copy_to_user((struct fscrypt_policy __user *)arg, &policy, sizeof(policy)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg)
|
||||
@ -1648,7 +1638,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
|
||||
struct f2fs_defragment *range)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_map_blocks map;
|
||||
struct f2fs_map_blocks map = { .m_next_pgofs = NULL };
|
||||
struct extent_info ei;
|
||||
pgoff_t pg_start, pg_end;
|
||||
unsigned int blk_per_seg = sbi->blocks_per_seg;
|
||||
@ -1874,14 +1864,32 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
|
||||
static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct inode *inode = file_inode(iocb->ki_filp);
|
||||
struct file *file = iocb->ki_filp;
|
||||
struct inode *inode = file_inode(file);
|
||||
ssize_t ret;
|
||||
|
||||
if (f2fs_encrypted_inode(inode) &&
|
||||
!f2fs_has_encryption_key(inode) &&
|
||||
f2fs_get_encryption_info(inode))
|
||||
!fscrypt_has_encryption_key(inode) &&
|
||||
fscrypt_get_encryption_info(inode))
|
||||
return -EACCES;
|
||||
|
||||
return generic_file_write_iter(iocb, from);
|
||||
inode_lock(inode);
|
||||
ret = generic_write_checks(iocb, from);
|
||||
if (ret > 0) {
|
||||
ret = f2fs_preallocate_blocks(iocb, from);
|
||||
if (!ret)
|
||||
ret = __generic_file_write_iter(iocb, from);
|
||||
}
|
||||
inode_unlock(inode);
|
||||
|
||||
if (ret > 0) {
|
||||
ssize_t err;
|
||||
|
||||
err = generic_write_sync(file, iocb->ki_pos - ret, ret);
|
||||
if (err < 0)
|
||||
ret = err;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
243
fs/f2fs/gc.c
243
fs/f2fs/gc.c
@ -245,6 +245,18 @@ static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi,
|
||||
return get_cb_cost(sbi, segno);
|
||||
}
|
||||
|
||||
static unsigned int count_bits(const unsigned long *addr,
|
||||
unsigned int offset, unsigned int len)
|
||||
{
|
||||
unsigned int end = offset + len, sum = 0;
|
||||
|
||||
while (offset < end) {
|
||||
if (test_bit(offset++, addr))
|
||||
++sum;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called from two paths.
|
||||
* One is garbage collection and the other is SSR segment selection.
|
||||
@ -258,9 +270,9 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
||||
{
|
||||
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
|
||||
struct victim_sel_policy p;
|
||||
unsigned int secno, max_cost;
|
||||
unsigned int secno, max_cost, last_victim;
|
||||
unsigned int last_segment = MAIN_SEGS(sbi);
|
||||
int nsearched = 0;
|
||||
unsigned int nsearched = 0;
|
||||
|
||||
mutex_lock(&dirty_i->seglist_lock);
|
||||
|
||||
@ -273,6 +285,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
||||
if (p.max_search == 0)
|
||||
goto out;
|
||||
|
||||
last_victim = sbi->last_victim[p.gc_mode];
|
||||
if (p.alloc_mode == LFS && gc_type == FG_GC) {
|
||||
p.min_segno = check_bg_victims(sbi);
|
||||
if (p.min_segno != NULL_SEGNO)
|
||||
@ -295,27 +308,35 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
||||
}
|
||||
|
||||
p.offset = segno + p.ofs_unit;
|
||||
if (p.ofs_unit > 1)
|
||||
if (p.ofs_unit > 1) {
|
||||
p.offset -= segno % p.ofs_unit;
|
||||
nsearched += count_bits(p.dirty_segmap,
|
||||
p.offset - p.ofs_unit,
|
||||
p.ofs_unit);
|
||||
} else {
|
||||
nsearched++;
|
||||
}
|
||||
|
||||
|
||||
secno = GET_SECNO(sbi, segno);
|
||||
|
||||
if (sec_usage_check(sbi, secno))
|
||||
continue;
|
||||
goto next;
|
||||
if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap))
|
||||
continue;
|
||||
goto next;
|
||||
|
||||
cost = get_gc_cost(sbi, segno, &p);
|
||||
|
||||
if (p.min_cost > cost) {
|
||||
p.min_segno = segno;
|
||||
p.min_cost = cost;
|
||||
} else if (unlikely(cost == max_cost)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nsearched++ >= p.max_search) {
|
||||
sbi->last_victim[p.gc_mode] = segno;
|
||||
next:
|
||||
if (nsearched >= p.max_search) {
|
||||
if (!sbi->last_victim[p.gc_mode] && segno <= last_victim)
|
||||
sbi->last_victim[p.gc_mode] = last_victim + 1;
|
||||
else
|
||||
sbi->last_victim[p.gc_mode] = segno + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -399,7 +420,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi,
|
||||
* On validity, copy that node with cold status, otherwise (invalid node)
|
||||
* ignore that.
|
||||
*/
|
||||
static int gc_node_segment(struct f2fs_sb_info *sbi,
|
||||
static void gc_node_segment(struct f2fs_sb_info *sbi,
|
||||
struct f2fs_summary *sum, unsigned int segno, int gc_type)
|
||||
{
|
||||
bool initial = true;
|
||||
@ -419,7 +440,7 @@ next_step:
|
||||
|
||||
/* stop BG_GC if there is not enough free sections. */
|
||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if (check_valid_map(sbi, segno, off) == 0)
|
||||
continue;
|
||||
@ -446,7 +467,7 @@ next_step:
|
||||
|
||||
/* set page dirty and write it */
|
||||
if (gc_type == FG_GC) {
|
||||
f2fs_wait_on_page_writeback(node_page, NODE);
|
||||
f2fs_wait_on_page_writeback(node_page, NODE, true);
|
||||
set_page_dirty(node_page);
|
||||
} else {
|
||||
if (!PageWriteback(node_page))
|
||||
@ -460,20 +481,6 @@ next_step:
|
||||
initial = false;
|
||||
goto next_step;
|
||||
}
|
||||
|
||||
if (gc_type == FG_GC) {
|
||||
struct writeback_control wbc = {
|
||||
.sync_mode = WB_SYNC_ALL,
|
||||
.nr_to_write = LONG_MAX,
|
||||
.for_reclaim = 0,
|
||||
};
|
||||
sync_node_pages(sbi, 0, &wbc);
|
||||
|
||||
/* return 1 only if FG_GC succefully reclaimed one */
|
||||
if (get_valid_blocks(sbi, segno, 1) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -483,7 +490,7 @@ next_step:
|
||||
* as indirect or double indirect node blocks, are given, it must be a caller's
|
||||
* bug.
|
||||
*/
|
||||
block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi)
|
||||
block_t start_bidx_of_node(unsigned int node_ofs, struct inode *inode)
|
||||
{
|
||||
unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4;
|
||||
unsigned int bidx;
|
||||
@ -500,7 +507,7 @@ block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi)
|
||||
int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
|
||||
bidx = node_ofs - 5 - dec;
|
||||
}
|
||||
return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi);
|
||||
return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode);
|
||||
}
|
||||
|
||||
static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
@ -546,6 +553,7 @@ static void move_encrypted_block(struct inode *inode, block_t bidx)
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
struct page *page;
|
||||
block_t newaddr;
|
||||
int err;
|
||||
|
||||
/* do not read out */
|
||||
@ -567,21 +575,24 @@ static void move_encrypted_block(struct inode *inode, block_t bidx)
|
||||
* don't cache encrypted data into meta inode until previous dirty
|
||||
* data were writebacked to avoid racing between GC and flush.
|
||||
*/
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
|
||||
get_node_info(fio.sbi, dn.nid, &ni);
|
||||
set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
|
||||
|
||||
/* read page */
|
||||
fio.page = page;
|
||||
fio.blk_addr = dn.data_blkaddr;
|
||||
fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr;
|
||||
|
||||
fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi),
|
||||
fio.blk_addr,
|
||||
FGP_LOCK|FGP_CREAT,
|
||||
GFP_NOFS);
|
||||
if (!fio.encrypted_page)
|
||||
goto put_out;
|
||||
allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr,
|
||||
&sum, CURSEG_COLD_DATA);
|
||||
|
||||
fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), newaddr,
|
||||
FGP_LOCK | FGP_CREAT, GFP_NOFS);
|
||||
if (!fio.encrypted_page) {
|
||||
err = -ENOMEM;
|
||||
goto recover_block;
|
||||
}
|
||||
|
||||
err = f2fs_submit_page_bio(&fio);
|
||||
if (err)
|
||||
@ -590,33 +601,39 @@ static void move_encrypted_block(struct inode *inode, block_t bidx)
|
||||
/* write page */
|
||||
lock_page(fio.encrypted_page);
|
||||
|
||||
if (unlikely(!PageUptodate(fio.encrypted_page)))
|
||||
if (unlikely(!PageUptodate(fio.encrypted_page))) {
|
||||
err = -EIO;
|
||||
goto put_page_out;
|
||||
if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi)))
|
||||
}
|
||||
if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) {
|
||||
err = -EIO;
|
||||
goto put_page_out;
|
||||
}
|
||||
|
||||
set_page_dirty(fio.encrypted_page);
|
||||
f2fs_wait_on_page_writeback(fio.encrypted_page, DATA);
|
||||
f2fs_wait_on_page_writeback(fio.encrypted_page, DATA, true);
|
||||
if (clear_page_dirty_for_io(fio.encrypted_page))
|
||||
dec_page_count(fio.sbi, F2FS_DIRTY_META);
|
||||
|
||||
set_page_writeback(fio.encrypted_page);
|
||||
|
||||
/* allocate block address */
|
||||
f2fs_wait_on_page_writeback(dn.node_page, NODE);
|
||||
allocate_data_block(fio.sbi, NULL, fio.blk_addr,
|
||||
&fio.blk_addr, &sum, CURSEG_COLD_DATA);
|
||||
f2fs_wait_on_page_writeback(dn.node_page, NODE, true);
|
||||
|
||||
fio.rw = WRITE_SYNC;
|
||||
fio.new_blkaddr = newaddr;
|
||||
f2fs_submit_page_mbio(&fio);
|
||||
|
||||
dn.data_blkaddr = fio.blk_addr;
|
||||
set_data_blkaddr(&dn);
|
||||
f2fs_update_extent_cache(&dn);
|
||||
f2fs_update_data_blkaddr(&dn, newaddr);
|
||||
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
|
||||
if (page->index == 0)
|
||||
set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN);
|
||||
put_page_out:
|
||||
f2fs_put_page(fio.encrypted_page, 1);
|
||||
recover_block:
|
||||
if (err)
|
||||
__f2fs_replace_block(fio.sbi, &sum, newaddr, fio.old_blkaddr,
|
||||
true, true);
|
||||
put_out:
|
||||
f2fs_put_dnode(&dn);
|
||||
out:
|
||||
@ -645,7 +662,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
set_page_dirty(page);
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
if (clear_page_dirty_for_io(page))
|
||||
inode_dec_dirty_pages(inode);
|
||||
set_cold_data(page);
|
||||
@ -663,7 +680,7 @@ out:
|
||||
* If the parent node is not valid or the data block address is different,
|
||||
* the victim data block is ignored.
|
||||
*/
|
||||
static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
struct gc_inode_list *gc_list, unsigned int segno, int gc_type)
|
||||
{
|
||||
struct super_block *sb = sbi->sb;
|
||||
@ -686,7 +703,7 @@ next_step:
|
||||
|
||||
/* stop BG_GC if there is not enough free sections. */
|
||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if (check_valid_map(sbi, segno, off) == 0)
|
||||
continue;
|
||||
@ -719,7 +736,7 @@ next_step:
|
||||
continue;
|
||||
}
|
||||
|
||||
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode));
|
||||
start_bidx = start_bidx_of_node(nofs, inode);
|
||||
data_page = get_read_data_page(inode,
|
||||
start_bidx + ofs_in_node, READA, true);
|
||||
if (IS_ERR(data_page)) {
|
||||
@ -735,7 +752,7 @@ next_step:
|
||||
/* phase 3 */
|
||||
inode = find_gc_inode(gc_list, dni.ino);
|
||||
if (inode) {
|
||||
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode))
|
||||
start_bidx = start_bidx_of_node(nofs, inode)
|
||||
+ ofs_in_node;
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
move_encrypted_block(inode, start_bidx);
|
||||
@ -747,15 +764,6 @@ next_step:
|
||||
|
||||
if (++phase < 4)
|
||||
goto next_step;
|
||||
|
||||
if (gc_type == FG_GC) {
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
|
||||
/* return 1 only if FG_GC succefully reclaimed one */
|
||||
if (get_valid_blocks(sbi, segno, 1) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
|
||||
@ -771,53 +779,92 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
|
||||
static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
unsigned int start_segno,
|
||||
struct gc_inode_list *gc_list, int gc_type)
|
||||
{
|
||||
struct page *sum_page;
|
||||
struct f2fs_summary_block *sum;
|
||||
struct blk_plug plug;
|
||||
int nfree = 0;
|
||||
unsigned int segno = start_segno;
|
||||
unsigned int end_segno = start_segno + sbi->segs_per_sec;
|
||||
int seg_freed = 0;
|
||||
unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
|
||||
SUM_TYPE_DATA : SUM_TYPE_NODE;
|
||||
|
||||
/* read segment summary of victim */
|
||||
sum_page = get_sum_page(sbi, segno);
|
||||
/* readahead multi ssa blocks those have contiguous address */
|
||||
if (sbi->segs_per_sec > 1)
|
||||
ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno),
|
||||
sbi->segs_per_sec, META_SSA, true);
|
||||
|
||||
/* reference all summary page */
|
||||
while (segno < end_segno) {
|
||||
sum_page = get_sum_page(sbi, segno++);
|
||||
unlock_page(sum_page);
|
||||
}
|
||||
|
||||
blk_start_plug(&plug);
|
||||
|
||||
sum = page_address(sum_page);
|
||||
for (segno = start_segno; segno < end_segno; segno++) {
|
||||
/* find segment summary of victim */
|
||||
sum_page = find_get_page(META_MAPPING(sbi),
|
||||
GET_SUM_BLOCK(sbi, segno));
|
||||
f2fs_bug_on(sbi, !PageUptodate(sum_page));
|
||||
f2fs_put_page(sum_page, 0);
|
||||
|
||||
/*
|
||||
* this is to avoid deadlock:
|
||||
* - lock_page(sum_page) - f2fs_replace_block
|
||||
* - check_valid_map() - mutex_lock(sentry_lock)
|
||||
* - mutex_lock(sentry_lock) - change_curseg()
|
||||
* - lock_page(sum_page)
|
||||
*/
|
||||
unlock_page(sum_page);
|
||||
sum = page_address(sum_page);
|
||||
f2fs_bug_on(sbi, type != GET_SUM_TYPE((&sum->footer)));
|
||||
|
||||
switch (GET_SUM_TYPE((&sum->footer))) {
|
||||
case SUM_TYPE_NODE:
|
||||
nfree = gc_node_segment(sbi, sum->entries, segno, gc_type);
|
||||
break;
|
||||
case SUM_TYPE_DATA:
|
||||
nfree = gc_data_segment(sbi, sum->entries, gc_list,
|
||||
segno, gc_type);
|
||||
break;
|
||||
/*
|
||||
* this is to avoid deadlock:
|
||||
* - lock_page(sum_page) - f2fs_replace_block
|
||||
* - check_valid_map() - mutex_lock(sentry_lock)
|
||||
* - mutex_lock(sentry_lock) - change_curseg()
|
||||
* - lock_page(sum_page)
|
||||
*/
|
||||
|
||||
if (type == SUM_TYPE_NODE)
|
||||
gc_node_segment(sbi, sum->entries, segno, gc_type);
|
||||
else
|
||||
gc_data_segment(sbi, sum->entries, gc_list, segno,
|
||||
gc_type);
|
||||
|
||||
stat_inc_seg_count(sbi, type, gc_type);
|
||||
|
||||
f2fs_put_page(sum_page, 0);
|
||||
}
|
||||
|
||||
if (gc_type == FG_GC) {
|
||||
if (type == SUM_TYPE_NODE) {
|
||||
struct writeback_control wbc = {
|
||||
.sync_mode = WB_SYNC_ALL,
|
||||
.nr_to_write = LONG_MAX,
|
||||
.for_reclaim = 0,
|
||||
};
|
||||
sync_node_pages(sbi, 0, &wbc);
|
||||
} else {
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
blk_finish_plug(&plug);
|
||||
|
||||
stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)), gc_type);
|
||||
if (gc_type == FG_GC) {
|
||||
while (start_segno < end_segno)
|
||||
if (get_valid_blocks(sbi, start_segno++, 1) == 0)
|
||||
seg_freed++;
|
||||
}
|
||||
|
||||
stat_inc_call_count(sbi->stat_info);
|
||||
|
||||
f2fs_put_page(sum_page, 0);
|
||||
return nfree;
|
||||
return seg_freed;
|
||||
}
|
||||
|
||||
int f2fs_gc(struct f2fs_sb_info *sbi, bool sync)
|
||||
{
|
||||
unsigned int segno, i;
|
||||
unsigned int segno;
|
||||
int gc_type = sync ? FG_GC : BG_GC;
|
||||
int sec_freed = 0;
|
||||
int sec_freed = 0, seg_freed;
|
||||
int ret = -EINVAL;
|
||||
struct cp_control cpc;
|
||||
struct gc_inode_list gc_list = {
|
||||
@ -838,30 +885,24 @@ gc_more:
|
||||
|
||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed)) {
|
||||
gc_type = FG_GC;
|
||||
/*
|
||||
* If there is no victim and no prefree segment but still not
|
||||
* enough free sections, we should flush dent/node blocks and do
|
||||
* garbage collections.
|
||||
*/
|
||||
if (__get_victim(sbi, &segno, gc_type) || prefree_segments(sbi))
|
||||
write_checkpoint(sbi, &cpc);
|
||||
else if (has_not_enough_free_secs(sbi, 0))
|
||||
write_checkpoint(sbi, &cpc);
|
||||
}
|
||||
|
||||
if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type))
|
||||
goto stop;
|
||||
ret = 0;
|
||||
|
||||
/* readahead multi ssa blocks those have contiguous address */
|
||||
if (sbi->segs_per_sec > 1)
|
||||
ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), sbi->segs_per_sec,
|
||||
META_SSA, true);
|
||||
seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type);
|
||||
|
||||
for (i = 0; i < sbi->segs_per_sec; i++) {
|
||||
/*
|
||||
* for FG_GC case, halt gcing left segments once failed one
|
||||
* of segments in selected section to avoid long latency.
|
||||
*/
|
||||
if (!do_garbage_collect(sbi, segno + i, &gc_list, gc_type) &&
|
||||
gc_type == FG_GC)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == sbi->segs_per_sec && gc_type == FG_GC)
|
||||
if (gc_type == FG_GC && seg_freed == sbi->segs_per_sec)
|
||||
sec_freed++;
|
||||
|
||||
if (gc_type == FG_GC)
|
||||
|
@ -71,7 +71,7 @@ bool truncate_inline_inode(struct page *ipage, u64 from)
|
||||
|
||||
addr = inline_data_addr(ipage);
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
memset(addr + from, 0, MAX_INLINE_DATA - from);
|
||||
|
||||
return true;
|
||||
@ -105,7 +105,6 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page)
|
||||
|
||||
int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
||||
{
|
||||
void *src_addr, *dst_addr;
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(dn->inode),
|
||||
.type = DATA,
|
||||
@ -115,8 +114,6 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
||||
};
|
||||
int dirty, err;
|
||||
|
||||
f2fs_bug_on(F2FS_I_SB(dn->inode), page->index);
|
||||
|
||||
if (!f2fs_exist_data(dn->inode))
|
||||
goto clear_out;
|
||||
|
||||
@ -124,21 +121,9 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_bug_on(F2FS_P_SB(page), PageWriteback(page));
|
||||
|
||||
if (PageUptodate(page))
|
||||
goto no_update;
|
||||
|
||||
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
|
||||
|
||||
/* Copy the whole inline data block */
|
||||
src_addr = inline_data_addr(dn->inode_page);
|
||||
dst_addr = kmap_atomic(page);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
||||
flush_dcache_page(page);
|
||||
kunmap_atomic(dst_addr);
|
||||
SetPageUptodate(page);
|
||||
no_update:
|
||||
read_inline_data(page, dn->inode_page);
|
||||
set_page_dirty(page);
|
||||
|
||||
/* clear dirty state */
|
||||
@ -146,11 +131,9 @@ no_update:
|
||||
|
||||
/* write data page to try to make data consistent */
|
||||
set_page_writeback(page);
|
||||
fio.blk_addr = dn->data_blkaddr;
|
||||
fio.old_blkaddr = dn->data_blkaddr;
|
||||
write_data_page(dn, &fio);
|
||||
set_data_blkaddr(dn);
|
||||
f2fs_update_extent_cache(dn);
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
if (dirty)
|
||||
inode_dec_dirty_pages(dn->inode);
|
||||
|
||||
@ -159,6 +142,7 @@ no_update:
|
||||
|
||||
/* clear inline data and flag after data writeback */
|
||||
truncate_inline_inode(dn->inode_page, 0);
|
||||
clear_inline_node(dn->inode_page);
|
||||
clear_out:
|
||||
stat_dec_inline_inode(dn->inode);
|
||||
f2fs_clear_inline_inode(dn->inode);
|
||||
@ -223,7 +207,7 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page)
|
||||
|
||||
f2fs_bug_on(F2FS_I_SB(inode), page->index);
|
||||
|
||||
f2fs_wait_on_page_writeback(dn.inode_page, NODE);
|
||||
f2fs_wait_on_page_writeback(dn.inode_page, NODE, true);
|
||||
src_addr = kmap_atomic(page);
|
||||
dst_addr = inline_data_addr(dn.inode_page);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
||||
@ -233,6 +217,7 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page)
|
||||
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
|
||||
|
||||
sync_inode_page(&dn);
|
||||
clear_inline_node(dn.inode_page);
|
||||
f2fs_put_dnode(&dn);
|
||||
return 0;
|
||||
}
|
||||
@ -261,7 +246,7 @@ process_inline:
|
||||
ipage = get_node_page(sbi, inode->i_ino);
|
||||
f2fs_bug_on(sbi, IS_ERR(ipage));
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
src_addr = inline_data_addr(npage);
|
||||
dst_addr = inline_data_addr(ipage);
|
||||
@ -292,7 +277,7 @@ process_inline:
|
||||
}
|
||||
|
||||
struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
|
||||
struct f2fs_filename *fname, struct page **res_page)
|
||||
struct fscrypt_name *fname, struct page **res_page)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
|
||||
struct f2fs_inline_dentry *inline_dentry;
|
||||
@ -389,7 +374,7 @@ static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
|
||||
|
||||
dentry_blk = kmap_atomic(page);
|
||||
@ -469,7 +454,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
|
||||
}
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
name_hash = f2fs_dentry_hash(name);
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
||||
@ -507,7 +492,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
||||
int i;
|
||||
|
||||
lock_page(page);
|
||||
f2fs_wait_on_page_writeback(page, NODE);
|
||||
f2fs_wait_on_page_writeback(page, NODE, true);
|
||||
|
||||
inline_dentry = inline_data_addr(page);
|
||||
bit_pos = dentry - inline_dentry->dentry;
|
||||
@ -550,7 +535,7 @@ bool f2fs_empty_inline_dir(struct inode *dir)
|
||||
}
|
||||
|
||||
int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx,
|
||||
struct f2fs_str *fstr)
|
||||
struct fscrypt_str *fstr)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct f2fs_inline_dentry *inline_dentry = NULL;
|
||||
|
@ -83,7 +83,7 @@ static void __recover_inline_status(struct inode *inode, struct page *ipage)
|
||||
|
||||
while (start < end) {
|
||||
if (*start++) {
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
|
||||
set_raw_inline(F2FS_I(inode), F2FS_INODE(ipage));
|
||||
@ -227,7 +227,7 @@ int update_inode(struct inode *inode, struct page *node_page)
|
||||
{
|
||||
struct f2fs_inode *ri;
|
||||
|
||||
f2fs_wait_on_page_writeback(node_page, NODE);
|
||||
f2fs_wait_on_page_writeback(node_page, NODE, true);
|
||||
|
||||
ri = F2FS_INODE(node_page);
|
||||
|
||||
@ -263,6 +263,10 @@ int update_inode(struct inode *inode, struct page *node_page)
|
||||
set_cold_node(inode, node_page);
|
||||
clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
|
||||
|
||||
/* deleted inode */
|
||||
if (inode->i_nlink == 0)
|
||||
clear_inline_node(node_page);
|
||||
|
||||
return set_page_dirty(node_page);
|
||||
}
|
||||
|
||||
@ -320,7 +324,7 @@ void f2fs_evict_inode(struct inode *inode)
|
||||
|
||||
/* some remained atomic pages should discarded */
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
commit_inmem_pages(inode, true);
|
||||
drop_inmem_pages(inode);
|
||||
|
||||
trace_f2fs_evict_inode(inode);
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
@ -385,10 +389,7 @@ no_delete:
|
||||
}
|
||||
}
|
||||
out_clear:
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
if (fi->i_crypt_info)
|
||||
f2fs_free_encryption_info(inode, fi->i_crypt_info);
|
||||
#endif
|
||||
fscrypt_put_encryption_info(inode, NULL);
|
||||
clear_inode(inode);
|
||||
}
|
||||
|
||||
|
178
fs/f2fs/namei.c
178
fs/f2fs/namei.c
@ -169,7 +169,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
int err;
|
||||
|
||||
if (f2fs_encrypted_inode(dir) &&
|
||||
!f2fs_is_child_context_consistent_with_parent(dir, inode))
|
||||
!fscrypt_has_permitted_context(dir, inode))
|
||||
return -EPERM;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
@ -260,6 +260,22 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
struct page *page;
|
||||
nid_t ino;
|
||||
int err = 0;
|
||||
unsigned int root_ino = F2FS_ROOT_INO(F2FS_I_SB(dir));
|
||||
|
||||
if (f2fs_encrypted_inode(dir)) {
|
||||
int res = fscrypt_get_encryption_info(dir);
|
||||
|
||||
/*
|
||||
* DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is
|
||||
* created while the directory was encrypted and we
|
||||
* don't have access to the key.
|
||||
*/
|
||||
if (fscrypt_has_encryption_key(dir))
|
||||
fscrypt_set_encrypted_dentry(dentry);
|
||||
fscrypt_set_d_op(dentry);
|
||||
if (res && res != -ENOKEY)
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
|
||||
if (dentry->d_name.len > F2FS_NAME_LEN)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
@ -276,15 +292,29 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
|
||||
if ((dir->i_ino == root_ino) && f2fs_has_inline_dots(dir)) {
|
||||
err = __recover_dot_dentries(dir, root_ino);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (f2fs_has_inline_dots(inode)) {
|
||||
err = __recover_dot_dentries(inode, dir->i_ino);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
if (!IS_ERR(inode) && f2fs_encrypted_inode(dir) &&
|
||||
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
||||
!fscrypt_has_permitted_context(dir, inode)) {
|
||||
bool nokey = f2fs_encrypted_inode(inode) &&
|
||||
!fscrypt_has_encryption_key(inode);
|
||||
err = nokey ? -ENOKEY : -EPERM;
|
||||
goto err_out;
|
||||
}
|
||||
return d_splice_alias(inode, dentry);
|
||||
|
||||
err_out:
|
||||
iget_failed(inode);
|
||||
iput(inode);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
@ -345,13 +375,23 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct inode *inode;
|
||||
size_t len = strlen(symname);
|
||||
size_t p_len;
|
||||
char *p_str;
|
||||
struct f2fs_str disk_link = FSTR_INIT(NULL, 0);
|
||||
struct f2fs_encrypted_symlink_data *sd = NULL;
|
||||
struct fscrypt_str disk_link = FSTR_INIT((char *)symname, len + 1);
|
||||
struct fscrypt_symlink_data *sd = NULL;
|
||||
int err;
|
||||
|
||||
if (len > dir->i_sb->s_blocksize)
|
||||
if (f2fs_encrypted_inode(dir)) {
|
||||
err = fscrypt_get_encryption_info(dir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!fscrypt_has_encryption_key(dir))
|
||||
return -EPERM;
|
||||
|
||||
disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
|
||||
sizeof(struct fscrypt_symlink_data));
|
||||
}
|
||||
|
||||
if (disk_link.len > dir->i_sb->s_blocksize)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
|
||||
@ -374,42 +414,36 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
f2fs_unlock_op(sbi);
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
|
||||
if (f2fs_encrypted_inode(dir)) {
|
||||
if (f2fs_encrypted_inode(inode)) {
|
||||
struct qstr istr = QSTR_INIT(symname, len);
|
||||
struct fscrypt_str ostr;
|
||||
|
||||
err = f2fs_get_encryption_info(inode);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
err = f2fs_fname_crypto_alloc_buffer(inode, len, &disk_link);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
err = f2fs_fname_usr_to_disk(inode, &istr, &disk_link);
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
|
||||
p_len = encrypted_symlink_data_len(disk_link.len) + 1;
|
||||
|
||||
if (p_len > dir->i_sb->s_blocksize) {
|
||||
err = -ENAMETOOLONG;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
sd = kzalloc(p_len, GFP_NOFS);
|
||||
sd = kzalloc(disk_link.len, GFP_NOFS);
|
||||
if (!sd) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
memcpy(sd->encrypted_path, disk_link.name, disk_link.len);
|
||||
sd->len = cpu_to_le16(disk_link.len);
|
||||
p_str = (char *)sd;
|
||||
} else {
|
||||
p_len = len + 1;
|
||||
p_str = (char *)symname;
|
||||
|
||||
err = fscrypt_get_encryption_info(inode);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
if (!fscrypt_has_encryption_key(inode)) {
|
||||
err = -EPERM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ostr.name = sd->encrypted_path;
|
||||
ostr.len = disk_link.len;
|
||||
err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr);
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
|
||||
sd->len = cpu_to_le16(ostr.len);
|
||||
disk_link.name = (char *)sd;
|
||||
}
|
||||
|
||||
err = page_symlink(inode, p_str, p_len);
|
||||
err = page_symlink(inode, disk_link.name, disk_link.len);
|
||||
|
||||
err_out:
|
||||
d_instantiate(dentry, inode);
|
||||
@ -425,7 +459,8 @@ err_out:
|
||||
* performance regression.
|
||||
*/
|
||||
if (!err) {
|
||||
filemap_write_and_wait_range(inode->i_mapping, 0, p_len - 1);
|
||||
filemap_write_and_wait_range(inode->i_mapping, 0,
|
||||
disk_link.len - 1);
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
@ -434,7 +469,6 @@ err_out:
|
||||
}
|
||||
|
||||
kfree(sd);
|
||||
f2fs_fname_crypto_free_buffer(&disk_link);
|
||||
return err;
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
@ -582,7 +616,7 @@ out:
|
||||
static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
if (f2fs_encrypted_inode(dir)) {
|
||||
int err = f2fs_get_encryption_info(dir);
|
||||
int err = fscrypt_get_encryption_info(dir);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -608,11 +642,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct f2fs_dir_entry *old_dir_entry = NULL;
|
||||
struct f2fs_dir_entry *old_entry;
|
||||
struct f2fs_dir_entry *new_entry;
|
||||
bool is_old_inline = f2fs_has_inline_dentry(old_dir);
|
||||
int err = -ENOENT;
|
||||
|
||||
if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) &&
|
||||
!f2fs_is_child_context_consistent_with_parent(new_dir,
|
||||
old_inode)) {
|
||||
!fscrypt_has_permitted_context(new_dir, old_inode)) {
|
||||
err = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
@ -654,8 +688,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
if (err)
|
||||
goto put_out_dir;
|
||||
|
||||
if (update_dent_inode(old_inode, new_inode,
|
||||
&new_dentry->d_name)) {
|
||||
err = update_dent_inode(old_inode, new_inode,
|
||||
&new_dentry->d_name);
|
||||
if (err) {
|
||||
release_orphan_inode(sbi);
|
||||
goto put_out_dir;
|
||||
}
|
||||
@ -693,6 +728,26 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
inc_nlink(new_dir);
|
||||
update_inode_page(new_dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* old entry and new entry can locate in the same inline
|
||||
* dentry in inode, when attaching new entry in inline dentry,
|
||||
* it could force inline dentry conversion, after that,
|
||||
* old_entry and old_page will point to wrong address, in
|
||||
* order to avoid this, let's do the check and update here.
|
||||
*/
|
||||
if (is_old_inline && !f2fs_has_inline_dentry(old_dir)) {
|
||||
f2fs_put_page(old_page, 0);
|
||||
old_page = NULL;
|
||||
|
||||
old_entry = f2fs_find_entry(old_dir,
|
||||
&old_dentry->d_name, &old_page);
|
||||
if (!old_entry) {
|
||||
err = -EIO;
|
||||
f2fs_unlock_op(sbi);
|
||||
goto out_whiteout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
down_write(&F2FS_I(old_inode)->i_sem);
|
||||
@ -771,11 +826,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
int err = -ENOENT;
|
||||
|
||||
if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) &&
|
||||
(old_dir != new_dir) &&
|
||||
(!f2fs_is_child_context_consistent_with_parent(new_dir,
|
||||
old_inode) ||
|
||||
!f2fs_is_child_context_consistent_with_parent(old_dir,
|
||||
new_inode)))
|
||||
(old_dir != new_dir) &&
|
||||
(!fscrypt_has_permitted_context(new_dir, old_inode) ||
|
||||
!fscrypt_has_permitted_context(old_dir, new_inode)))
|
||||
return -EPERM;
|
||||
|
||||
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
|
||||
@ -937,16 +990,15 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry,
|
||||
return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
static const char *f2fs_encrypted_get_link(struct dentry *dentry,
|
||||
struct inode *inode,
|
||||
struct delayed_call *done)
|
||||
{
|
||||
struct page *cpage = NULL;
|
||||
char *caddr, *paddr = NULL;
|
||||
struct f2fs_str cstr = FSTR_INIT(NULL, 0);
|
||||
struct f2fs_str pstr = FSTR_INIT(NULL, 0);
|
||||
struct f2fs_encrypted_symlink_data *sd;
|
||||
struct fscrypt_str cstr = FSTR_INIT(NULL, 0);
|
||||
struct fscrypt_str pstr = FSTR_INIT(NULL, 0);
|
||||
struct fscrypt_symlink_data *sd;
|
||||
loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
|
||||
u32 max_size = inode->i_sb->s_blocksize;
|
||||
int res;
|
||||
@ -954,7 +1006,7 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
|
||||
if (!dentry)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
res = f2fs_get_encryption_info(inode);
|
||||
res = fscrypt_get_encryption_info(inode);
|
||||
if (res)
|
||||
return ERR_PTR(res);
|
||||
|
||||
@ -965,7 +1017,8 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
|
||||
caddr[size] = 0;
|
||||
|
||||
/* Symlink is encrypted */
|
||||
sd = (struct f2fs_encrypted_symlink_data *)caddr;
|
||||
sd = (struct fscrypt_symlink_data *)caddr;
|
||||
cstr.name = sd->encrypted_path;
|
||||
cstr.len = le16_to_cpu(sd->len);
|
||||
|
||||
/* this is broken symlink case */
|
||||
@ -973,12 +1026,6 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
|
||||
res = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
cstr.name = kmalloc(cstr.len, GFP_NOFS);
|
||||
if (!cstr.name) {
|
||||
res = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
memcpy(cstr.name, sd->encrypted_path, cstr.len);
|
||||
|
||||
/* this is broken symlink case */
|
||||
if (unlikely(cstr.name[0] == 0)) {
|
||||
@ -986,22 +1033,19 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if ((cstr.len + sizeof(struct f2fs_encrypted_symlink_data) - 1) >
|
||||
max_size) {
|
||||
if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) {
|
||||
/* Symlink data on the disk is corrupted */
|
||||
res = -EIO;
|
||||
goto errout;
|
||||
}
|
||||
res = f2fs_fname_crypto_alloc_buffer(inode, cstr.len, &pstr);
|
||||
res = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
|
||||
if (res)
|
||||
goto errout;
|
||||
|
||||
res = f2fs_fname_disk_to_usr(inode, NULL, &cstr, &pstr);
|
||||
res = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
|
||||
if (res < 0)
|
||||
goto errout;
|
||||
|
||||
kfree(cstr.name);
|
||||
|
||||
paddr = pstr.name;
|
||||
|
||||
/* Null-terminate the name */
|
||||
@ -1011,8 +1055,7 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
|
||||
set_delayed_call(done, kfree_link, paddr);
|
||||
return paddr;
|
||||
errout:
|
||||
kfree(cstr.name);
|
||||
f2fs_fname_crypto_free_buffer(&pstr);
|
||||
fscrypt_fname_free_buffer(&pstr);
|
||||
page_cache_release(cpage);
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
@ -1029,7 +1072,6 @@ const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
|
||||
.removexattr = generic_removexattr,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
const struct inode_operations f2fs_dir_inode_operations = {
|
||||
.create = f2fs_create,
|
||||
|
223
fs/f2fs/node.c
223
fs/f2fs/node.c
@ -257,15 +257,20 @@ static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid)
|
||||
return new;
|
||||
}
|
||||
|
||||
static void cache_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid,
|
||||
static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid,
|
||||
struct f2fs_nat_entry *ne)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct nat_entry *e;
|
||||
|
||||
e = __lookup_nat_cache(nm_i, nid);
|
||||
if (!e) {
|
||||
e = grab_nat_entry(nm_i, nid);
|
||||
node_info_from_raw_nat(&e->ni, ne);
|
||||
} else {
|
||||
f2fs_bug_on(sbi, nat_get_ino(e) != ne->ino ||
|
||||
nat_get_blkaddr(e) != ne->block_addr ||
|
||||
nat_get_version(e) != ne->version);
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,7 +359,7 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
||||
struct f2fs_summary_block *sum = curseg->sum_blk;
|
||||
struct f2fs_journal *journal = curseg->journal;
|
||||
nid_t start_nid = START_NID(nid);
|
||||
struct f2fs_nat_block *nat_blk;
|
||||
struct page *page = NULL;
|
||||
@ -371,23 +376,20 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)
|
||||
ni->ino = nat_get_ino(e);
|
||||
ni->blk_addr = nat_get_blkaddr(e);
|
||||
ni->version = nat_get_version(e);
|
||||
}
|
||||
up_read(&nm_i->nat_tree_lock);
|
||||
if (e)
|
||||
up_read(&nm_i->nat_tree_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&ne, 0, sizeof(struct f2fs_nat_entry));
|
||||
|
||||
down_write(&nm_i->nat_tree_lock);
|
||||
|
||||
/* Check current segment summary */
|
||||
mutex_lock(&curseg->curseg_mutex);
|
||||
i = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 0);
|
||||
down_read(&curseg->journal_rwsem);
|
||||
i = lookup_journal_in_cursum(journal, NAT_JOURNAL, nid, 0);
|
||||
if (i >= 0) {
|
||||
ne = nat_in_journal(sum, i);
|
||||
ne = nat_in_journal(journal, i);
|
||||
node_info_from_raw_nat(ni, &ne);
|
||||
}
|
||||
mutex_unlock(&curseg->curseg_mutex);
|
||||
up_read(&curseg->journal_rwsem);
|
||||
if (i >= 0)
|
||||
goto cache;
|
||||
|
||||
@ -398,19 +400,52 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)
|
||||
node_info_from_raw_nat(ni, &ne);
|
||||
f2fs_put_page(page, 1);
|
||||
cache:
|
||||
up_read(&nm_i->nat_tree_lock);
|
||||
/* cache nat entry */
|
||||
cache_nat_entry(NM_I(sbi), nid, &ne);
|
||||
down_write(&nm_i->nat_tree_lock);
|
||||
cache_nat_entry(sbi, nid, &ne);
|
||||
up_write(&nm_i->nat_tree_lock);
|
||||
}
|
||||
|
||||
pgoff_t get_next_page_offset(struct dnode_of_data *dn, pgoff_t pgofs)
|
||||
{
|
||||
const long direct_index = ADDRS_PER_INODE(dn->inode);
|
||||
const long direct_blks = ADDRS_PER_BLOCK;
|
||||
const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
|
||||
unsigned int skipped_unit = ADDRS_PER_BLOCK;
|
||||
int cur_level = dn->cur_level;
|
||||
int max_level = dn->max_level;
|
||||
pgoff_t base = 0;
|
||||
|
||||
if (!dn->max_level)
|
||||
return pgofs + 1;
|
||||
|
||||
while (max_level-- > cur_level)
|
||||
skipped_unit *= NIDS_PER_BLOCK;
|
||||
|
||||
switch (dn->max_level) {
|
||||
case 3:
|
||||
base += 2 * indirect_blks;
|
||||
case 2:
|
||||
base += 2 * direct_blks;
|
||||
case 1:
|
||||
base += direct_index;
|
||||
break;
|
||||
default:
|
||||
f2fs_bug_on(F2FS_I_SB(dn->inode), 1);
|
||||
}
|
||||
|
||||
return ((pgofs - base) / skipped_unit + 1) * skipped_unit + base;
|
||||
}
|
||||
|
||||
/*
|
||||
* The maximum depth is four.
|
||||
* Offset[0] will have raw inode offset.
|
||||
*/
|
||||
static int get_node_path(struct f2fs_inode_info *fi, long block,
|
||||
static int get_node_path(struct inode *inode, long block,
|
||||
int offset[4], unsigned int noffset[4])
|
||||
{
|
||||
const long direct_index = ADDRS_PER_INODE(fi);
|
||||
const long direct_index = ADDRS_PER_INODE(inode);
|
||||
const long direct_blks = ADDRS_PER_BLOCK;
|
||||
const long dptrs_per_blk = NIDS_PER_BLOCK;
|
||||
const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
|
||||
@ -495,10 +530,10 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode)
|
||||
int offset[4];
|
||||
unsigned int noffset[4];
|
||||
nid_t nids[4];
|
||||
int level, i;
|
||||
int level, i = 0;
|
||||
int err = 0;
|
||||
|
||||
level = get_node_path(F2FS_I(dn->inode), index, offset, noffset);
|
||||
level = get_node_path(dn->inode, index, offset, noffset);
|
||||
|
||||
nids[0] = dn->inode->i_ino;
|
||||
npage[0] = dn->inode_page;
|
||||
@ -585,6 +620,10 @@ release_pages:
|
||||
release_out:
|
||||
dn->inode_page = NULL;
|
||||
dn->node_page = NULL;
|
||||
if (err == -ENOENT) {
|
||||
dn->cur_level = i;
|
||||
dn->max_level = level;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -792,7 +831,7 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from)
|
||||
|
||||
trace_f2fs_truncate_inode_blocks_enter(inode, from);
|
||||
|
||||
level = get_node_path(F2FS_I(inode), from, offset, noffset);
|
||||
level = get_node_path(inode, from, offset, noffset);
|
||||
restart:
|
||||
page = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(page)) {
|
||||
@ -861,7 +900,7 @@ skip_partial:
|
||||
f2fs_put_page(page, 1);
|
||||
goto restart;
|
||||
}
|
||||
f2fs_wait_on_page_writeback(page, NODE);
|
||||
f2fs_wait_on_page_writeback(page, NODE, true);
|
||||
ri->i_nid[offset[0] - NODE_DIR1_BLOCK] = 0;
|
||||
set_page_dirty(page);
|
||||
unlock_page(page);
|
||||
@ -976,7 +1015,7 @@ struct page *new_node_page(struct dnode_of_data *dn,
|
||||
new_ni.ino = dn->inode->i_ino;
|
||||
set_node_addr(sbi, &new_ni, NEW_ADDR, false);
|
||||
|
||||
f2fs_wait_on_page_writeback(page, NODE);
|
||||
f2fs_wait_on_page_writeback(page, NODE, true);
|
||||
fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true);
|
||||
set_cold_node(dn->inode, page);
|
||||
SetPageUptodate(page);
|
||||
@ -1029,7 +1068,7 @@ static int read_node_page(struct page *page, int rw)
|
||||
if (PageUptodate(page))
|
||||
return LOCKED_PAGE;
|
||||
|
||||
fio.blk_addr = ni.blk_addr;
|
||||
fio.new_blkaddr = fio.old_blkaddr = ni.blk_addr;
|
||||
return f2fs_submit_page_bio(&fio);
|
||||
}
|
||||
|
||||
@ -1045,12 +1084,11 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
|
||||
return;
|
||||
f2fs_bug_on(sbi, check_nid_range(sbi, nid));
|
||||
|
||||
apage = find_get_page(NODE_MAPPING(sbi), nid);
|
||||
if (apage && PageUptodate(apage)) {
|
||||
f2fs_put_page(apage, 0);
|
||||
rcu_read_lock();
|
||||
apage = radix_tree_lookup(&NODE_MAPPING(sbi)->page_tree, nid);
|
||||
rcu_read_unlock();
|
||||
if (apage)
|
||||
return;
|
||||
}
|
||||
f2fs_put_page(apage, 0);
|
||||
|
||||
apage = grab_cache_page(NODE_MAPPING(sbi), nid);
|
||||
if (!apage)
|
||||
@ -1063,7 +1101,7 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
|
||||
/*
|
||||
* readahead MAX_RA_NODE number of node pages.
|
||||
*/
|
||||
void ra_node_pages(struct page *parent, int start)
|
||||
static void ra_node_pages(struct page *parent, int start)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_P_SB(parent);
|
||||
struct blk_plug plug;
|
||||
@ -1083,7 +1121,7 @@ void ra_node_pages(struct page *parent, int start)
|
||||
blk_finish_plug(&plug);
|
||||
}
|
||||
|
||||
struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid,
|
||||
static struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid,
|
||||
struct page *parent, int start)
|
||||
{
|
||||
struct page *page;
|
||||
@ -1154,19 +1192,57 @@ void sync_inode_page(struct dnode_of_data *dn)
|
||||
dn->node_changed = ret ? true: false;
|
||||
}
|
||||
|
||||
static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct page *page;
|
||||
|
||||
/* should flush inline_data before evict_inode */
|
||||
inode = ilookup(sbi->sb, ino);
|
||||
if (!inode)
|
||||
return;
|
||||
|
||||
page = pagecache_get_page(inode->i_mapping, 0, FGP_NOWAIT, 0);
|
||||
if (!page)
|
||||
goto iput_out;
|
||||
|
||||
if (!trylock_page(page))
|
||||
goto release_out;
|
||||
|
||||
if (!PageUptodate(page))
|
||||
goto page_out;
|
||||
|
||||
if (!PageDirty(page))
|
||||
goto page_out;
|
||||
|
||||
if (!clear_page_dirty_for_io(page))
|
||||
goto page_out;
|
||||
|
||||
if (!f2fs_write_inline_data(inode, page))
|
||||
inode_dec_dirty_pages(inode);
|
||||
else
|
||||
set_page_dirty(page);
|
||||
page_out:
|
||||
unlock_page(page);
|
||||
release_out:
|
||||
f2fs_put_page(page, 0);
|
||||
iput_out:
|
||||
iput(inode);
|
||||
}
|
||||
|
||||
int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
pgoff_t index, end;
|
||||
struct pagevec pvec;
|
||||
int step = ino ? 2 : 0;
|
||||
int nwritten = 0, wrote = 0;
|
||||
int nwritten = 0;
|
||||
|
||||
pagevec_init(&pvec, 0);
|
||||
|
||||
next_step:
|
||||
index = 0;
|
||||
end = LONG_MAX;
|
||||
end = ULONG_MAX;
|
||||
|
||||
while (index <= end) {
|
||||
int i, nr_pages;
|
||||
@ -1203,6 +1279,7 @@ next_step:
|
||||
* If an fsync mode,
|
||||
* we should not skip writing node pages.
|
||||
*/
|
||||
lock_node:
|
||||
if (ino && ino_of_node(page) == ino)
|
||||
lock_page(page);
|
||||
else if (!trylock_page(page))
|
||||
@ -1221,6 +1298,17 @@ continue_unlock:
|
||||
goto continue_unlock;
|
||||
}
|
||||
|
||||
/* flush inline_data */
|
||||
if (!ino && is_inline_node(page)) {
|
||||
clear_inline_node(page);
|
||||
unlock_page(page);
|
||||
flush_inline_data(sbi, ino_of_node(page));
|
||||
goto lock_node;
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(page, NODE, true);
|
||||
|
||||
BUG_ON(PageWriteback(page));
|
||||
if (!clear_page_dirty_for_io(page))
|
||||
goto continue_unlock;
|
||||
|
||||
@ -1238,8 +1326,6 @@ continue_unlock:
|
||||
|
||||
if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc))
|
||||
unlock_page(page);
|
||||
else
|
||||
wrote++;
|
||||
|
||||
if (--wbc->nr_to_write == 0)
|
||||
break;
|
||||
@ -1257,15 +1343,12 @@ continue_unlock:
|
||||
step++;
|
||||
goto next_step;
|
||||
}
|
||||
|
||||
if (wrote)
|
||||
f2fs_submit_merged_bio(sbi, NODE, WRITE);
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino)
|
||||
{
|
||||
pgoff_t index = 0, end = LONG_MAX;
|
||||
pgoff_t index = 0, end = ULONG_MAX;
|
||||
struct pagevec pvec;
|
||||
int ret2 = 0, ret = 0;
|
||||
|
||||
@ -1287,7 +1370,7 @@ int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino)
|
||||
continue;
|
||||
|
||||
if (ino && ino_of_node(page) == ino) {
|
||||
f2fs_wait_on_page_writeback(page, NODE);
|
||||
f2fs_wait_on_page_writeback(page, NODE, true);
|
||||
if (TestClearPageError(page))
|
||||
ret = -EIO;
|
||||
}
|
||||
@ -1326,8 +1409,6 @@ static int f2fs_write_node_page(struct page *page,
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
goto redirty_out;
|
||||
|
||||
f2fs_wait_on_page_writeback(page, NODE);
|
||||
|
||||
/* get old block addr of this node page */
|
||||
nid = nid_of_node(page);
|
||||
f2fs_bug_on(sbi, page->index != nid);
|
||||
@ -1351,14 +1432,18 @@ static int f2fs_write_node_page(struct page *page,
|
||||
}
|
||||
|
||||
set_page_writeback(page);
|
||||
fio.blk_addr = ni.blk_addr;
|
||||
fio.old_blkaddr = ni.blk_addr;
|
||||
write_node_page(nid, &fio);
|
||||
set_node_addr(sbi, &ni, fio.blk_addr, is_fsync_dnode(page));
|
||||
set_node_addr(sbi, &ni, fio.new_blkaddr, is_fsync_dnode(page));
|
||||
dec_page_count(sbi, F2FS_DIRTY_NODES);
|
||||
up_read(&sbi->node_write);
|
||||
|
||||
if (wbc->for_reclaim)
|
||||
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, NODE, WRITE);
|
||||
|
||||
unlock_page(page);
|
||||
|
||||
if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi)))
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
f2fs_submit_merged_bio(sbi, NODE, WRITE);
|
||||
|
||||
return 0;
|
||||
@ -1374,8 +1459,6 @@ static int f2fs_write_node_pages(struct address_space *mapping,
|
||||
struct f2fs_sb_info *sbi = F2FS_M_SB(mapping);
|
||||
long diff;
|
||||
|
||||
trace_f2fs_writepages(mapping->host, wbc, NODE);
|
||||
|
||||
/* balancing f2fs's metadata in background */
|
||||
f2fs_balance_fs_bg(sbi);
|
||||
|
||||
@ -1383,6 +1466,8 @@ static int f2fs_write_node_pages(struct address_space *mapping,
|
||||
if (get_pages(sbi, F2FS_DIRTY_NODES) < nr_pages_to_skip(sbi, NODE))
|
||||
goto skip_write;
|
||||
|
||||
trace_f2fs_writepages(mapping->host, wbc, NODE);
|
||||
|
||||
diff = nr_pages_to_write(sbi, NODE, wbc);
|
||||
wbc->sync_mode = WB_SYNC_NONE;
|
||||
sync_node_pages(sbi, 0, wbc);
|
||||
@ -1391,6 +1476,7 @@ static int f2fs_write_node_pages(struct address_space *mapping,
|
||||
|
||||
skip_write:
|
||||
wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_NODES);
|
||||
trace_f2fs_writepages(mapping->host, wbc, NODE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1526,7 +1612,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
||||
struct f2fs_summary_block *sum = curseg->sum_blk;
|
||||
struct f2fs_journal *journal = curseg->journal;
|
||||
int i = 0;
|
||||
nid_t nid = nm_i->next_scan_nid;
|
||||
|
||||
@ -1558,16 +1644,18 @@ static void build_free_nids(struct f2fs_sb_info *sbi)
|
||||
nm_i->next_scan_nid = nid;
|
||||
|
||||
/* find free nids from current sum_pages */
|
||||
mutex_lock(&curseg->curseg_mutex);
|
||||
for (i = 0; i < nats_in_cursum(sum); i++) {
|
||||
block_t addr = le32_to_cpu(nat_in_journal(sum, i).block_addr);
|
||||
nid = le32_to_cpu(nid_in_journal(sum, i));
|
||||
down_read(&curseg->journal_rwsem);
|
||||
for (i = 0; i < nats_in_cursum(journal); i++) {
|
||||
block_t addr;
|
||||
|
||||
addr = le32_to_cpu(nat_in_journal(journal, i).block_addr);
|
||||
nid = le32_to_cpu(nid_in_journal(journal, i));
|
||||
if (addr == NULL_ADDR)
|
||||
add_free_nid(sbi, nid, true);
|
||||
else
|
||||
remove_free_nid(nm_i, nid);
|
||||
}
|
||||
mutex_unlock(&curseg->curseg_mutex);
|
||||
up_read(&curseg->journal_rwsem);
|
||||
up_read(&nm_i->nat_tree_lock);
|
||||
|
||||
ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid),
|
||||
@ -1703,7 +1791,7 @@ void recover_inline_xattr(struct inode *inode, struct page *page)
|
||||
src_addr = inline_xattr_addr(page);
|
||||
inline_size = inline_xattr_size(inode);
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
memcpy(dst_addr, src_addr, inline_size);
|
||||
update_inode:
|
||||
update_inode(inode, ipage);
|
||||
@ -1831,16 +1919,16 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
||||
struct f2fs_summary_block *sum = curseg->sum_blk;
|
||||
struct f2fs_journal *journal = curseg->journal;
|
||||
int i;
|
||||
|
||||
mutex_lock(&curseg->curseg_mutex);
|
||||
for (i = 0; i < nats_in_cursum(sum); i++) {
|
||||
down_write(&curseg->journal_rwsem);
|
||||
for (i = 0; i < nats_in_cursum(journal); i++) {
|
||||
struct nat_entry *ne;
|
||||
struct f2fs_nat_entry raw_ne;
|
||||
nid_t nid = le32_to_cpu(nid_in_journal(sum, i));
|
||||
nid_t nid = le32_to_cpu(nid_in_journal(journal, i));
|
||||
|
||||
raw_ne = nat_in_journal(sum, i);
|
||||
raw_ne = nat_in_journal(journal, i);
|
||||
|
||||
ne = __lookup_nat_cache(nm_i, nid);
|
||||
if (!ne) {
|
||||
@ -1849,8 +1937,8 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi)
|
||||
}
|
||||
__set_nat_cache_dirty(nm_i, ne);
|
||||
}
|
||||
update_nats_in_cursum(sum, -i);
|
||||
mutex_unlock(&curseg->curseg_mutex);
|
||||
update_nats_in_cursum(journal, -i);
|
||||
up_write(&curseg->journal_rwsem);
|
||||
}
|
||||
|
||||
static void __adjust_nat_entry_set(struct nat_entry_set *nes,
|
||||
@ -1875,7 +1963,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
|
||||
struct nat_entry_set *set)
|
||||
{
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
||||
struct f2fs_summary_block *sum = curseg->sum_blk;
|
||||
struct f2fs_journal *journal = curseg->journal;
|
||||
nid_t start_nid = set->set * NAT_ENTRY_PER_BLOCK;
|
||||
bool to_journal = true;
|
||||
struct f2fs_nat_block *nat_blk;
|
||||
@ -1887,11 +1975,11 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
|
||||
* #1, flush nat entries to journal in current hot data summary block.
|
||||
* #2, flush nat entries to nat page.
|
||||
*/
|
||||
if (!__has_cursum_space(sum, set->entry_cnt, NAT_JOURNAL))
|
||||
if (!__has_cursum_space(journal, set->entry_cnt, NAT_JOURNAL))
|
||||
to_journal = false;
|
||||
|
||||
if (to_journal) {
|
||||
mutex_lock(&curseg->curseg_mutex);
|
||||
down_write(&curseg->journal_rwsem);
|
||||
} else {
|
||||
page = get_next_nat_page(sbi, start_nid);
|
||||
nat_blk = page_address(page);
|
||||
@ -1908,11 +1996,11 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
|
||||
continue;
|
||||
|
||||
if (to_journal) {
|
||||
offset = lookup_journal_in_cursum(sum,
|
||||
offset = lookup_journal_in_cursum(journal,
|
||||
NAT_JOURNAL, nid, 1);
|
||||
f2fs_bug_on(sbi, offset < 0);
|
||||
raw_ne = &nat_in_journal(sum, offset);
|
||||
nid_in_journal(sum, offset) = cpu_to_le32(nid);
|
||||
raw_ne = &nat_in_journal(journal, offset);
|
||||
nid_in_journal(journal, offset) = cpu_to_le32(nid);
|
||||
} else {
|
||||
raw_ne = &nat_blk->entries[nid - start_nid];
|
||||
}
|
||||
@ -1924,7 +2012,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
|
||||
}
|
||||
|
||||
if (to_journal)
|
||||
mutex_unlock(&curseg->curseg_mutex);
|
||||
up_write(&curseg->journal_rwsem);
|
||||
else
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
@ -1941,7 +2029,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
||||
struct f2fs_summary_block *sum = curseg->sum_blk;
|
||||
struct f2fs_journal *journal = curseg->journal;
|
||||
struct nat_entry_set *setvec[SETVEC_SIZE];
|
||||
struct nat_entry_set *set, *tmp;
|
||||
unsigned int found;
|
||||
@ -1958,7 +2046,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
|
||||
* entries, remove all entries from journal and merge them
|
||||
* into nat entry set.
|
||||
*/
|
||||
if (!__has_cursum_space(sum, nm_i->dirty_nat_cnt, NAT_JOURNAL))
|
||||
if (!__has_cursum_space(journal, nm_i->dirty_nat_cnt, NAT_JOURNAL))
|
||||
remove_nats_in_journal(sbi);
|
||||
|
||||
while ((found = __gang_lookup_nat_set(nm_i,
|
||||
@ -1967,7 +2055,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
|
||||
set_idx = setvec[found - 1]->set + 1;
|
||||
for (idx = 0; idx < found; idx++)
|
||||
__adjust_nat_entry_set(setvec[idx], &sets,
|
||||
MAX_NAT_JENTRIES(sum));
|
||||
MAX_NAT_JENTRIES(journal));
|
||||
}
|
||||
|
||||
/* flush dirty nats in nat entry set */
|
||||
@ -2000,6 +2088,7 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
|
||||
nm_i->nat_cnt = 0;
|
||||
nm_i->ram_thresh = DEF_RAM_THRESHOLD;
|
||||
nm_i->ra_nid_pages = DEF_RA_NID_PAGES;
|
||||
nm_i->dirty_nats_ratio = DEF_DIRTY_NAT_RATIO_THRESHOLD;
|
||||
|
||||
INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC);
|
||||
INIT_LIST_HEAD(&nm_i->free_nid_list);
|
||||
|
@ -25,6 +25,9 @@
|
||||
/* control the memory footprint threshold (10MB per 1GB ram) */
|
||||
#define DEF_RAM_THRESHOLD 10
|
||||
|
||||
/* control dirty nats ratio threshold (default: 10% over max nid count) */
|
||||
#define DEF_DIRTY_NAT_RATIO_THRESHOLD 10
|
||||
|
||||
/* vector size for gang look-up from nat cache that consists of radix tree */
|
||||
#define NATVEC_SIZE 64
|
||||
#define SETVEC_SIZE 32
|
||||
@ -117,6 +120,12 @@ static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne,
|
||||
raw_ne->version = ni->version;
|
||||
}
|
||||
|
||||
static inline bool excess_dirty_nats(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return NM_I(sbi)->dirty_nat_cnt >= NM_I(sbi)->max_nid *
|
||||
NM_I(sbi)->dirty_nats_ratio / 100;
|
||||
}
|
||||
|
||||
enum mem_type {
|
||||
FREE_NIDS, /* indicates the free nid list */
|
||||
NAT_ENTRIES, /* indicates the cached nat entry */
|
||||
@ -321,7 +330,7 @@ static inline int set_nid(struct page *p, int off, nid_t nid, bool i)
|
||||
{
|
||||
struct f2fs_node *rn = F2FS_NODE(p);
|
||||
|
||||
f2fs_wait_on_page_writeback(p, NODE);
|
||||
f2fs_wait_on_page_writeback(p, NODE, true);
|
||||
|
||||
if (i)
|
||||
rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid);
|
||||
@ -370,6 +379,21 @@ static inline int is_node(struct page *page, int type)
|
||||
#define is_fsync_dnode(page) is_node(page, FSYNC_BIT_SHIFT)
|
||||
#define is_dent_dnode(page) is_node(page, DENT_BIT_SHIFT)
|
||||
|
||||
static inline int is_inline_node(struct page *page)
|
||||
{
|
||||
return PageChecked(page);
|
||||
}
|
||||
|
||||
static inline void set_inline_node(struct page *page)
|
||||
{
|
||||
SetPageChecked(page);
|
||||
}
|
||||
|
||||
static inline void clear_inline_node(struct page *page)
|
||||
{
|
||||
ClearPageChecked(page);
|
||||
}
|
||||
|
||||
static inline void set_cold_node(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct f2fs_node *rn = F2FS_NODE(page);
|
||||
|
@ -350,8 +350,7 @@ got_it:
|
||||
inode = dn->inode;
|
||||
}
|
||||
|
||||
bidx = start_bidx_of_node(offset, F2FS_I(inode)) +
|
||||
le16_to_cpu(sum.ofs_in_node);
|
||||
bidx = start_bidx_of_node(offset, inode) + le16_to_cpu(sum.ofs_in_node);
|
||||
|
||||
/*
|
||||
* if inode page is locked, unlock temporarily, but its reference
|
||||
@ -386,10 +385,9 @@ truncate_out:
|
||||
static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
struct page *page, block_t blkaddr)
|
||||
{
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
unsigned int start, end;
|
||||
struct dnode_of_data dn;
|
||||
struct node_info ni;
|
||||
unsigned int start, end;
|
||||
int err = 0, recovered = 0;
|
||||
|
||||
/* step 1: recover xattr */
|
||||
@ -409,8 +407,8 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
goto out;
|
||||
|
||||
/* step 3: recover data indices */
|
||||
start = start_bidx_of_node(ofs_of_node(page), fi);
|
||||
end = start + ADDRS_PER_PAGE(page, fi);
|
||||
start = start_bidx_of_node(ofs_of_node(page), inode);
|
||||
end = start + ADDRS_PER_PAGE(page, inode);
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
|
||||
@ -418,7 +416,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
f2fs_wait_on_page_writeback(dn.node_page, NODE);
|
||||
f2fs_wait_on_page_writeback(dn.node_page, NODE, true);
|
||||
|
||||
get_node_info(sbi, dn.nid, &ni);
|
||||
f2fs_bug_on(sbi, ni.ino != ino_of_node(page));
|
||||
@ -467,7 +465,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
|
||||
/* write dummy data page */
|
||||
f2fs_replace_block(sbi, &dn, src, dest,
|
||||
ni.version, false);
|
||||
ni.version, false, false);
|
||||
recovered++;
|
||||
}
|
||||
}
|
||||
|
@ -191,70 +191,145 @@ void register_inmem_page(struct inode *inode, struct page *page)
|
||||
trace_f2fs_register_inmem_page(page, INMEM);
|
||||
}
|
||||
|
||||
int commit_inmem_pages(struct inode *inode, bool abort)
|
||||
static int __revoke_inmem_pages(struct inode *inode,
|
||||
struct list_head *head, bool drop, bool recover)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct inmem_pages *cur, *tmp;
|
||||
int err = 0;
|
||||
|
||||
list_for_each_entry_safe(cur, tmp, head, list) {
|
||||
struct page *page = cur->page;
|
||||
|
||||
if (drop)
|
||||
trace_f2fs_commit_inmem_page(page, INMEM_DROP);
|
||||
|
||||
lock_page(page);
|
||||
|
||||
if (recover) {
|
||||
struct dnode_of_data dn;
|
||||
struct node_info ni;
|
||||
|
||||
trace_f2fs_commit_inmem_page(page, INMEM_REVOKE);
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
if (get_dnode_of_data(&dn, page->index, LOOKUP_NODE)) {
|
||||
err = -EAGAIN;
|
||||
goto next;
|
||||
}
|
||||
get_node_info(sbi, dn.nid, &ni);
|
||||
f2fs_replace_block(sbi, &dn, dn.data_blkaddr,
|
||||
cur->old_addr, ni.version, true, true);
|
||||
f2fs_put_dnode(&dn);
|
||||
}
|
||||
next:
|
||||
ClearPageUptodate(page);
|
||||
set_page_private(page, 0);
|
||||
ClearPageUptodate(page);
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
list_del(&cur->list);
|
||||
kmem_cache_free(inmem_entry_slab, cur);
|
||||
dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void drop_inmem_pages(struct inode *inode)
|
||||
{
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
|
||||
mutex_lock(&fi->inmem_lock);
|
||||
__revoke_inmem_pages(inode, &fi->inmem_pages, true, false);
|
||||
mutex_unlock(&fi->inmem_lock);
|
||||
}
|
||||
|
||||
static int __commit_inmem_pages(struct inode *inode,
|
||||
struct list_head *revoke_list)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
struct inmem_pages *cur, *tmp;
|
||||
bool submit_bio = false;
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = sbi,
|
||||
.type = DATA,
|
||||
.rw = WRITE_SYNC | REQ_PRIO,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
bool submit_bio = false;
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* The abort is true only when f2fs_evict_inode is called.
|
||||
* Basically, the f2fs_evict_inode doesn't produce any data writes, so
|
||||
* that we don't need to call f2fs_balance_fs.
|
||||
* Otherwise, f2fs_gc in f2fs_balance_fs can wait forever until this
|
||||
* inode becomes free by iget_locked in f2fs_iget.
|
||||
*/
|
||||
if (!abort) {
|
||||
f2fs_balance_fs(sbi, true);
|
||||
f2fs_lock_op(sbi);
|
||||
list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) {
|
||||
struct page *page = cur->page;
|
||||
|
||||
lock_page(page);
|
||||
if (page->mapping == inode->i_mapping) {
|
||||
trace_f2fs_commit_inmem_page(page, INMEM);
|
||||
|
||||
set_page_dirty(page);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
if (clear_page_dirty_for_io(page))
|
||||
inode_dec_dirty_pages(inode);
|
||||
|
||||
fio.page = page;
|
||||
err = do_write_data_page(&fio);
|
||||
if (err) {
|
||||
unlock_page(page);
|
||||
break;
|
||||
}
|
||||
|
||||
/* record old blkaddr for revoking */
|
||||
cur->old_addr = fio.old_blkaddr;
|
||||
|
||||
clear_cold_data(page);
|
||||
submit_bio = true;
|
||||
}
|
||||
unlock_page(page);
|
||||
list_move_tail(&cur->list, revoke_list);
|
||||
}
|
||||
|
||||
mutex_lock(&fi->inmem_lock);
|
||||
list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) {
|
||||
lock_page(cur->page);
|
||||
if (!abort) {
|
||||
if (cur->page->mapping == inode->i_mapping) {
|
||||
set_page_dirty(cur->page);
|
||||
f2fs_wait_on_page_writeback(cur->page, DATA);
|
||||
if (clear_page_dirty_for_io(cur->page))
|
||||
inode_dec_dirty_pages(inode);
|
||||
trace_f2fs_commit_inmem_page(cur->page, INMEM);
|
||||
fio.page = cur->page;
|
||||
err = do_write_data_page(&fio);
|
||||
if (err) {
|
||||
unlock_page(cur->page);
|
||||
break;
|
||||
}
|
||||
clear_cold_data(cur->page);
|
||||
submit_bio = true;
|
||||
}
|
||||
} else {
|
||||
ClearPageUptodate(cur->page);
|
||||
trace_f2fs_commit_inmem_page(cur->page, INMEM_DROP);
|
||||
}
|
||||
set_page_private(cur->page, 0);
|
||||
ClearPagePrivate(cur->page);
|
||||
f2fs_put_page(cur->page, 1);
|
||||
if (submit_bio)
|
||||
f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE);
|
||||
|
||||
list_del(&cur->list);
|
||||
kmem_cache_free(inmem_entry_slab, cur);
|
||||
dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
|
||||
if (!err)
|
||||
__revoke_inmem_pages(inode, revoke_list, false, false);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int commit_inmem_pages(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
struct list_head revoke_list;
|
||||
int err;
|
||||
|
||||
INIT_LIST_HEAD(&revoke_list);
|
||||
f2fs_balance_fs(sbi, true);
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
mutex_lock(&fi->inmem_lock);
|
||||
err = __commit_inmem_pages(inode, &revoke_list);
|
||||
if (err) {
|
||||
int ret;
|
||||
/*
|
||||
* try to revoke all committed pages, but still we could fail
|
||||
* due to no memory or other reason, if that happened, EAGAIN
|
||||
* will be returned, which means in such case, transaction is
|
||||
* already not integrity, caller should use journal to do the
|
||||
* recovery or rewrite & commit last transaction. For other
|
||||
* error number, revoking was done by filesystem itself.
|
||||
*/
|
||||
ret = __revoke_inmem_pages(inode, &revoke_list, false, true);
|
||||
if (ret)
|
||||
err = ret;
|
||||
|
||||
/* drop all uncommitted pages */
|
||||
__revoke_inmem_pages(inode, &fi->inmem_pages, true, false);
|
||||
}
|
||||
mutex_unlock(&fi->inmem_lock);
|
||||
|
||||
if (!abort) {
|
||||
f2fs_unlock_op(sbi);
|
||||
if (submit_bio)
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
}
|
||||
f2fs_unlock_op(sbi);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -291,11 +366,17 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
|
||||
|
||||
/* checkpoint is the only way to shrink partial cached entries */
|
||||
if (!available_free_memory(sbi, NAT_ENTRIES) ||
|
||||
excess_prefree_segs(sbi) ||
|
||||
!available_free_memory(sbi, INO_ENTRIES) ||
|
||||
excess_prefree_segs(sbi) ||
|
||||
excess_dirty_nats(sbi) ||
|
||||
(is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) {
|
||||
if (test_opt(sbi, DATA_FLUSH))
|
||||
if (test_opt(sbi, DATA_FLUSH)) {
|
||||
struct blk_plug plug;
|
||||
|
||||
blk_start_plug(&plug);
|
||||
sync_dirty_inodes(sbi, FILE_INODE);
|
||||
blk_finish_plug(&plug);
|
||||
}
|
||||
f2fs_sync_fs(sbi->sb, true);
|
||||
stat_inc_bg_cp_count(sbi->stat_info);
|
||||
}
|
||||
@ -502,7 +583,7 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi,
|
||||
|
||||
bool discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr)
|
||||
{
|
||||
int err = -ENOTSUPP;
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
if (test_opt(sbi, DISCARD)) {
|
||||
struct seg_entry *se = get_seg_entry(sbi,
|
||||
@ -841,6 +922,31 @@ static void write_sum_page(struct f2fs_sb_info *sbi,
|
||||
update_meta_page(sbi, (void *)sum_blk, blk_addr);
|
||||
}
|
||||
|
||||
static void write_current_sum_page(struct f2fs_sb_info *sbi,
|
||||
int type, block_t blk_addr)
|
||||
{
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, type);
|
||||
struct page *page = grab_meta_page(sbi, blk_addr);
|
||||
struct f2fs_summary_block *src = curseg->sum_blk;
|
||||
struct f2fs_summary_block *dst;
|
||||
|
||||
dst = (struct f2fs_summary_block *)page_address(page);
|
||||
|
||||
mutex_lock(&curseg->curseg_mutex);
|
||||
|
||||
down_read(&curseg->journal_rwsem);
|
||||
memcpy(&dst->journal, curseg->journal, SUM_JOURNAL_SIZE);
|
||||
up_read(&curseg->journal_rwsem);
|
||||
|
||||
memcpy(dst->entries, src->entries, SUM_ENTRY_SIZE);
|
||||
memcpy(&dst->footer, &src->footer, SUM_FOOTER_SIZE);
|
||||
|
||||
mutex_unlock(&curseg->curseg_mutex);
|
||||
|
||||
set_page_dirty(page);
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
static int is_next_segment_free(struct f2fs_sb_info *sbi, int type)
|
||||
{
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, type);
|
||||
@ -873,9 +979,8 @@ static void get_new_segment(struct f2fs_sb_info *sbi,
|
||||
|
||||
if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) {
|
||||
segno = find_next_zero_bit(free_i->free_segmap,
|
||||
MAIN_SEGS(sbi), *newseg + 1);
|
||||
if (segno - *newseg < sbi->segs_per_sec -
|
||||
(*newseg % sbi->segs_per_sec))
|
||||
(hint + 1) * sbi->segs_per_sec, *newseg + 1);
|
||||
if (segno < (hint + 1) * sbi->segs_per_sec)
|
||||
goto got_it;
|
||||
}
|
||||
find_other_zone:
|
||||
@ -1280,8 +1385,8 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio)
|
||||
{
|
||||
int type = __get_segment_type(fio->page, fio->type);
|
||||
|
||||
allocate_data_block(fio->sbi, fio->page, fio->blk_addr,
|
||||
&fio->blk_addr, sum, type);
|
||||
allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
|
||||
&fio->new_blkaddr, sum, type);
|
||||
|
||||
/* writeout dirty page into bdev */
|
||||
f2fs_submit_page_mbio(fio);
|
||||
@ -1293,7 +1398,8 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page)
|
||||
.sbi = sbi,
|
||||
.type = META,
|
||||
.rw = WRITE_SYNC | REQ_META | REQ_PRIO,
|
||||
.blk_addr = page->index,
|
||||
.old_blkaddr = page->index,
|
||||
.new_blkaddr = page->index,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
@ -1323,19 +1429,19 @@ void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio)
|
||||
get_node_info(sbi, dn->nid, &ni);
|
||||
set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
|
||||
do_write_page(&sum, fio);
|
||||
dn->data_blkaddr = fio->blk_addr;
|
||||
f2fs_update_data_blkaddr(dn, fio->new_blkaddr);
|
||||
}
|
||||
|
||||
void rewrite_data_page(struct f2fs_io_info *fio)
|
||||
{
|
||||
fio->new_blkaddr = fio->old_blkaddr;
|
||||
stat_inc_inplace_blocks(fio->sbi);
|
||||
f2fs_submit_page_mbio(fio);
|
||||
}
|
||||
|
||||
static void __f2fs_replace_block(struct f2fs_sb_info *sbi,
|
||||
struct f2fs_summary *sum,
|
||||
void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
block_t old_blkaddr, block_t new_blkaddr,
|
||||
bool recover_curseg)
|
||||
bool recover_curseg, bool recover_newaddr)
|
||||
{
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
struct curseg_info *curseg;
|
||||
@ -1378,7 +1484,7 @@ static void __f2fs_replace_block(struct f2fs_sb_info *sbi,
|
||||
curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr);
|
||||
__add_sum_entry(sbi, type, sum);
|
||||
|
||||
if (!recover_curseg)
|
||||
if (!recover_curseg || recover_newaddr)
|
||||
update_sit_entry(sbi, new_blkaddr, 1);
|
||||
if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
|
||||
update_sit_entry(sbi, old_blkaddr, -1);
|
||||
@ -1402,66 +1508,30 @@ static void __f2fs_replace_block(struct f2fs_sb_info *sbi,
|
||||
|
||||
void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
|
||||
block_t old_addr, block_t new_addr,
|
||||
unsigned char version, bool recover_curseg)
|
||||
unsigned char version, bool recover_curseg,
|
||||
bool recover_newaddr)
|
||||
{
|
||||
struct f2fs_summary sum;
|
||||
|
||||
set_summary(&sum, dn->nid, dn->ofs_in_node, version);
|
||||
|
||||
__f2fs_replace_block(sbi, &sum, old_addr, new_addr, recover_curseg);
|
||||
__f2fs_replace_block(sbi, &sum, old_addr, new_addr,
|
||||
recover_curseg, recover_newaddr);
|
||||
|
||||
dn->data_blkaddr = new_addr;
|
||||
set_data_blkaddr(dn);
|
||||
f2fs_update_extent_cache(dn);
|
||||
}
|
||||
|
||||
static inline bool is_merged_page(struct f2fs_sb_info *sbi,
|
||||
struct page *page, enum page_type type)
|
||||
{
|
||||
enum page_type btype = PAGE_TYPE_OF_BIO(type);
|
||||
struct f2fs_bio_info *io = &sbi->write_io[btype];
|
||||
struct bio_vec *bvec;
|
||||
struct page *target;
|
||||
int i;
|
||||
|
||||
down_read(&io->io_rwsem);
|
||||
if (!io->bio) {
|
||||
up_read(&io->io_rwsem);
|
||||
return false;
|
||||
}
|
||||
|
||||
bio_for_each_segment_all(bvec, io->bio, i) {
|
||||
|
||||
if (bvec->bv_page->mapping) {
|
||||
target = bvec->bv_page;
|
||||
} else {
|
||||
struct f2fs_crypto_ctx *ctx;
|
||||
|
||||
/* encrypted page */
|
||||
ctx = (struct f2fs_crypto_ctx *)page_private(
|
||||
bvec->bv_page);
|
||||
target = ctx->w.control_page;
|
||||
}
|
||||
|
||||
if (page == target) {
|
||||
up_read(&io->io_rwsem);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
up_read(&io->io_rwsem);
|
||||
return false;
|
||||
f2fs_update_data_blkaddr(dn, new_addr);
|
||||
}
|
||||
|
||||
void f2fs_wait_on_page_writeback(struct page *page,
|
||||
enum page_type type)
|
||||
enum page_type type, bool ordered)
|
||||
{
|
||||
if (PageWriteback(page)) {
|
||||
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
|
||||
|
||||
if (is_merged_page(sbi, page, type))
|
||||
f2fs_submit_merged_bio(sbi, type, WRITE);
|
||||
wait_on_page_writeback(page);
|
||||
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, type, WRITE);
|
||||
if (ordered)
|
||||
wait_on_page_writeback(page);
|
||||
else
|
||||
wait_for_stable_page(page);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1477,7 +1547,7 @@ void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi,
|
||||
|
||||
cpage = find_lock_page(META_MAPPING(sbi), blkaddr);
|
||||
if (cpage) {
|
||||
f2fs_wait_on_page_writeback(cpage, DATA);
|
||||
f2fs_wait_on_page_writeback(cpage, DATA, true);
|
||||
f2fs_put_page(cpage, 1);
|
||||
}
|
||||
}
|
||||
@ -1498,12 +1568,11 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi)
|
||||
|
||||
/* Step 1: restore nat cache */
|
||||
seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
||||
memcpy(&seg_i->sum_blk->n_nats, kaddr, SUM_JOURNAL_SIZE);
|
||||
memcpy(seg_i->journal, kaddr, SUM_JOURNAL_SIZE);
|
||||
|
||||
/* Step 2: restore sit cache */
|
||||
seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
||||
memcpy(&seg_i->sum_blk->n_sits, kaddr + SUM_JOURNAL_SIZE,
|
||||
SUM_JOURNAL_SIZE);
|
||||
memcpy(seg_i->journal, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE);
|
||||
offset = 2 * SUM_JOURNAL_SIZE;
|
||||
|
||||
/* Step 3: restore summary entries */
|
||||
@ -1599,7 +1668,14 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
|
||||
/* set uncompleted segment to curseg */
|
||||
curseg = CURSEG_I(sbi, type);
|
||||
mutex_lock(&curseg->curseg_mutex);
|
||||
memcpy(curseg->sum_blk, sum, PAGE_CACHE_SIZE);
|
||||
|
||||
/* update journal info */
|
||||
down_write(&curseg->journal_rwsem);
|
||||
memcpy(curseg->journal, &sum->journal, SUM_JOURNAL_SIZE);
|
||||
up_write(&curseg->journal_rwsem);
|
||||
|
||||
memcpy(curseg->sum_blk->entries, sum->entries, SUM_ENTRY_SIZE);
|
||||
memcpy(&curseg->sum_blk->footer, &sum->footer, SUM_FOOTER_SIZE);
|
||||
curseg->next_segno = segno;
|
||||
reset_curseg(sbi, type, 0);
|
||||
curseg->alloc_type = ckpt->alloc_type[type];
|
||||
@ -1654,13 +1730,12 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr)
|
||||
|
||||
/* Step 1: write nat cache */
|
||||
seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
||||
memcpy(kaddr, &seg_i->sum_blk->n_nats, SUM_JOURNAL_SIZE);
|
||||
memcpy(kaddr, seg_i->journal, SUM_JOURNAL_SIZE);
|
||||
written_size += SUM_JOURNAL_SIZE;
|
||||
|
||||
/* Step 2: write sit cache */
|
||||
seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
||||
memcpy(kaddr + written_size, &seg_i->sum_blk->n_sits,
|
||||
SUM_JOURNAL_SIZE);
|
||||
memcpy(kaddr + written_size, seg_i->journal, SUM_JOURNAL_SIZE);
|
||||
written_size += SUM_JOURNAL_SIZE;
|
||||
|
||||
/* Step 3: write summary entries */
|
||||
@ -1706,12 +1781,8 @@ static void write_normal_summaries(struct f2fs_sb_info *sbi,
|
||||
else
|
||||
end = type + NR_CURSEG_NODE_TYPE;
|
||||
|
||||
for (i = type; i < end; i++) {
|
||||
struct curseg_info *sum = CURSEG_I(sbi, i);
|
||||
mutex_lock(&sum->curseg_mutex);
|
||||
write_sum_page(sbi, sum->sum_blk, blkaddr + (i - type));
|
||||
mutex_unlock(&sum->curseg_mutex);
|
||||
}
|
||||
for (i = type; i < end; i++)
|
||||
write_current_sum_page(sbi, i, blkaddr + (i - type));
|
||||
}
|
||||
|
||||
void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk)
|
||||
@ -1727,24 +1798,24 @@ void write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk)
|
||||
write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE);
|
||||
}
|
||||
|
||||
int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type,
|
||||
int lookup_journal_in_cursum(struct f2fs_journal *journal, int type,
|
||||
unsigned int val, int alloc)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (type == NAT_JOURNAL) {
|
||||
for (i = 0; i < nats_in_cursum(sum); i++) {
|
||||
if (le32_to_cpu(nid_in_journal(sum, i)) == val)
|
||||
for (i = 0; i < nats_in_cursum(journal); i++) {
|
||||
if (le32_to_cpu(nid_in_journal(journal, i)) == val)
|
||||
return i;
|
||||
}
|
||||
if (alloc && __has_cursum_space(sum, 1, NAT_JOURNAL))
|
||||
return update_nats_in_cursum(sum, 1);
|
||||
if (alloc && __has_cursum_space(journal, 1, NAT_JOURNAL))
|
||||
return update_nats_in_cursum(journal, 1);
|
||||
} else if (type == SIT_JOURNAL) {
|
||||
for (i = 0; i < sits_in_cursum(sum); i++)
|
||||
if (le32_to_cpu(segno_in_journal(sum, i)) == val)
|
||||
for (i = 0; i < sits_in_cursum(journal); i++)
|
||||
if (le32_to_cpu(segno_in_journal(journal, i)) == val)
|
||||
return i;
|
||||
if (alloc && __has_cursum_space(sum, 1, SIT_JOURNAL))
|
||||
return update_sits_in_cursum(sum, 1);
|
||||
if (alloc && __has_cursum_space(journal, 1, SIT_JOURNAL))
|
||||
return update_sits_in_cursum(journal, 1);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -1848,20 +1919,22 @@ static void add_sits_in_set(struct f2fs_sb_info *sbi)
|
||||
static void remove_sits_in_journal(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
||||
struct f2fs_summary_block *sum = curseg->sum_blk;
|
||||
struct f2fs_journal *journal = curseg->journal;
|
||||
int i;
|
||||
|
||||
for (i = sits_in_cursum(sum) - 1; i >= 0; i--) {
|
||||
down_write(&curseg->journal_rwsem);
|
||||
for (i = 0; i < sits_in_cursum(journal); i++) {
|
||||
unsigned int segno;
|
||||
bool dirtied;
|
||||
|
||||
segno = le32_to_cpu(segno_in_journal(sum, i));
|
||||
segno = le32_to_cpu(segno_in_journal(journal, i));
|
||||
dirtied = __mark_sit_entry_dirty(sbi, segno);
|
||||
|
||||
if (!dirtied)
|
||||
add_sit_entry(segno, &SM_I(sbi)->sit_entry_set);
|
||||
}
|
||||
update_sits_in_cursum(sum, -sits_in_cursum(sum));
|
||||
update_sits_in_cursum(journal, -i);
|
||||
up_write(&curseg->journal_rwsem);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1873,13 +1946,12 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
unsigned long *bitmap = sit_i->dirty_sentries_bitmap;
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
||||
struct f2fs_summary_block *sum = curseg->sum_blk;
|
||||
struct f2fs_journal *journal = curseg->journal;
|
||||
struct sit_entry_set *ses, *tmp;
|
||||
struct list_head *head = &SM_I(sbi)->sit_entry_set;
|
||||
bool to_journal = true;
|
||||
struct seg_entry *se;
|
||||
|
||||
mutex_lock(&curseg->curseg_mutex);
|
||||
mutex_lock(&sit_i->sentry_lock);
|
||||
|
||||
if (!sit_i->dirty_sentries)
|
||||
@ -1896,7 +1968,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
* entries, remove all entries from journal and add and account
|
||||
* them in sit entry set.
|
||||
*/
|
||||
if (!__has_cursum_space(sum, sit_i->dirty_sentries, SIT_JOURNAL))
|
||||
if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL))
|
||||
remove_sits_in_journal(sbi);
|
||||
|
||||
/*
|
||||
@ -1913,10 +1985,12 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
unsigned int segno = start_segno;
|
||||
|
||||
if (to_journal &&
|
||||
!__has_cursum_space(sum, ses->entry_cnt, SIT_JOURNAL))
|
||||
!__has_cursum_space(journal, ses->entry_cnt, SIT_JOURNAL))
|
||||
to_journal = false;
|
||||
|
||||
if (!to_journal) {
|
||||
if (to_journal) {
|
||||
down_write(&curseg->journal_rwsem);
|
||||
} else {
|
||||
page = get_next_sit_page(sbi, start_segno);
|
||||
raw_sit = page_address(page);
|
||||
}
|
||||
@ -1934,13 +2008,13 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
}
|
||||
|
||||
if (to_journal) {
|
||||
offset = lookup_journal_in_cursum(sum,
|
||||
offset = lookup_journal_in_cursum(journal,
|
||||
SIT_JOURNAL, segno, 1);
|
||||
f2fs_bug_on(sbi, offset < 0);
|
||||
segno_in_journal(sum, offset) =
|
||||
segno_in_journal(journal, offset) =
|
||||
cpu_to_le32(segno);
|
||||
seg_info_to_raw_sit(se,
|
||||
&sit_in_journal(sum, offset));
|
||||
&sit_in_journal(journal, offset));
|
||||
} else {
|
||||
sit_offset = SIT_ENTRY_OFFSET(sit_i, segno);
|
||||
seg_info_to_raw_sit(se,
|
||||
@ -1952,7 +2026,9 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
ses->entry_cnt--;
|
||||
}
|
||||
|
||||
if (!to_journal)
|
||||
if (to_journal)
|
||||
up_write(&curseg->journal_rwsem);
|
||||
else
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
f2fs_bug_on(sbi, ses->entry_cnt);
|
||||
@ -1967,7 +2043,6 @@ out:
|
||||
add_discard_addrs(sbi, cpc);
|
||||
}
|
||||
mutex_unlock(&sit_i->sentry_lock);
|
||||
mutex_unlock(&curseg->curseg_mutex);
|
||||
|
||||
set_prefree_as_free_segments(sbi);
|
||||
}
|
||||
@ -2099,6 +2174,11 @@ static int build_curseg(struct f2fs_sb_info *sbi)
|
||||
array[i].sum_blk = kzalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
|
||||
if (!array[i].sum_blk)
|
||||
return -ENOMEM;
|
||||
init_rwsem(&array[i].journal_rwsem);
|
||||
array[i].journal = kzalloc(sizeof(struct f2fs_journal),
|
||||
GFP_KERNEL);
|
||||
if (!array[i].journal)
|
||||
return -ENOMEM;
|
||||
array[i].segno = NULL_SEGNO;
|
||||
array[i].next_blkoff = 0;
|
||||
}
|
||||
@ -2109,11 +2189,11 @@ static void build_sit_entries(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
||||
struct f2fs_summary_block *sum = curseg->sum_blk;
|
||||
struct f2fs_journal *journal = curseg->journal;
|
||||
int sit_blk_cnt = SIT_BLK_CNT(sbi);
|
||||
unsigned int i, start, end;
|
||||
unsigned int readed, start_blk = 0;
|
||||
int nrpages = MAX_BIO_BLOCKS(sbi);
|
||||
int nrpages = MAX_BIO_BLOCKS(sbi) * 8;
|
||||
|
||||
do {
|
||||
readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT, true);
|
||||
@ -2127,16 +2207,16 @@ static void build_sit_entries(struct f2fs_sb_info *sbi)
|
||||
struct f2fs_sit_entry sit;
|
||||
struct page *page;
|
||||
|
||||
mutex_lock(&curseg->curseg_mutex);
|
||||
for (i = 0; i < sits_in_cursum(sum); i++) {
|
||||
if (le32_to_cpu(segno_in_journal(sum, i))
|
||||
down_read(&curseg->journal_rwsem);
|
||||
for (i = 0; i < sits_in_cursum(journal); i++) {
|
||||
if (le32_to_cpu(segno_in_journal(journal, i))
|
||||
== start) {
|
||||
sit = sit_in_journal(sum, i);
|
||||
mutex_unlock(&curseg->curseg_mutex);
|
||||
sit = sit_in_journal(journal, i);
|
||||
up_read(&curseg->journal_rwsem);
|
||||
goto got_it;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&curseg->curseg_mutex);
|
||||
up_read(&curseg->journal_rwsem);
|
||||
|
||||
page = get_current_sit_page(sbi, start);
|
||||
sit_blk = (struct f2fs_sit_block *)page_address(page);
|
||||
@ -2371,8 +2451,10 @@ static void destroy_curseg(struct f2fs_sb_info *sbi)
|
||||
if (!array)
|
||||
return;
|
||||
SM_I(sbi)->curseg_array = NULL;
|
||||
for (i = 0; i < NR_CURSEG_TYPE; i++)
|
||||
for (i = 0; i < NR_CURSEG_TYPE; i++) {
|
||||
kfree(array[i].sum_blk);
|
||||
kfree(array[i].journal);
|
||||
}
|
||||
kfree(array);
|
||||
}
|
||||
|
||||
|
@ -183,7 +183,7 @@ struct segment_allocation {
|
||||
* this value is set in page as a private data which indicate that
|
||||
* the page is atomically written, and it is in inmem_pages list.
|
||||
*/
|
||||
#define ATOMIC_WRITTEN_PAGE 0x0000ffff
|
||||
#define ATOMIC_WRITTEN_PAGE ((unsigned long)-1)
|
||||
|
||||
#define IS_ATOMIC_WRITTEN_PAGE(page) \
|
||||
(page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE)
|
||||
@ -191,6 +191,7 @@ struct segment_allocation {
|
||||
struct inmem_pages {
|
||||
struct list_head list;
|
||||
struct page *page;
|
||||
block_t old_addr; /* for revoking when fail to commit */
|
||||
};
|
||||
|
||||
struct sit_info {
|
||||
@ -257,6 +258,8 @@ struct victim_selection {
|
||||
struct curseg_info {
|
||||
struct mutex curseg_mutex; /* lock for consistency */
|
||||
struct f2fs_summary_block *sum_blk; /* cached summary block */
|
||||
struct rw_semaphore journal_rwsem; /* protect journal area */
|
||||
struct f2fs_journal *journal; /* cached journal info */
|
||||
unsigned char alloc_type; /* current allocation type */
|
||||
unsigned int segno; /* current segment number */
|
||||
unsigned short next_blkoff; /* next block offset to write */
|
||||
|
206
fs/f2fs/super.c
206
fs/f2fs/super.c
@ -126,6 +126,19 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
struct super_block *sb = sbi->sb;
|
||||
|
||||
if (!sb->s_bdev->bd_part)
|
||||
return snprintf(buf, PAGE_SIZE, "0\n");
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
(unsigned long long)(sbi->kbytes_written +
|
||||
BD_PART_WRITTEN(sbi)));
|
||||
}
|
||||
|
||||
static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
@ -204,6 +217,9 @@ static struct f2fs_attr f2fs_attr_##_name = { \
|
||||
f2fs_sbi_show, f2fs_sbi_store, \
|
||||
offsetof(struct struct_name, elname))
|
||||
|
||||
#define F2FS_GENERAL_RO_ATTR(name) \
|
||||
static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL)
|
||||
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time);
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time);
|
||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time);
|
||||
@ -216,10 +232,12 @@ F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util);
|
||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks);
|
||||
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh);
|
||||
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages);
|
||||
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, dirty_nats_ratio, dirty_nats_ratio);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
|
||||
F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes);
|
||||
|
||||
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
|
||||
static struct attribute *f2fs_attrs[] = {
|
||||
@ -237,8 +255,10 @@ static struct attribute *f2fs_attrs[] = {
|
||||
ATTR_LIST(dir_level),
|
||||
ATTR_LIST(ram_thresh),
|
||||
ATTR_LIST(ra_nid_pages),
|
||||
ATTR_LIST(dirty_nats_ratio),
|
||||
ATTR_LIST(cp_interval),
|
||||
ATTR_LIST(idle_interval),
|
||||
ATTR_LIST(lifetime_write_kbytes),
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -450,10 +470,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
|
||||
|
||||
/* Will be used by directory only */
|
||||
fi->i_dir_level = F2FS_SB(sb)->dir_level;
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
fi->i_crypt_info = NULL;
|
||||
#endif
|
||||
return &fi->vfs_inode;
|
||||
}
|
||||
|
||||
@ -474,7 +490,7 @@ static int f2fs_drop_inode(struct inode *inode)
|
||||
|
||||
/* some remained atomic pages should discarded */
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
commit_inmem_pages(inode, true);
|
||||
drop_inmem_pages(inode);
|
||||
|
||||
/* should remain fi->extent_tree for writepage */
|
||||
f2fs_destroy_extent_node(inode);
|
||||
@ -487,11 +503,7 @@ static int f2fs_drop_inode(struct inode *inode)
|
||||
|
||||
sb_end_intwrite(inode->i_sb);
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
if (F2FS_I(inode)->i_crypt_info)
|
||||
f2fs_free_encryption_info(inode,
|
||||
F2FS_I(inode)->i_crypt_info);
|
||||
#endif
|
||||
fscrypt_put_encryption_info(inode, NULL);
|
||||
spin_lock(&inode->i_lock);
|
||||
atomic_dec(&inode->i_count);
|
||||
}
|
||||
@ -562,6 +574,10 @@ static void f2fs_put_super(struct super_block *sb)
|
||||
f2fs_leave_shrinker(sbi);
|
||||
mutex_unlock(&sbi->umount_mutex);
|
||||
|
||||
/* our cp_error case, we can wait for any writeback page */
|
||||
if (get_pages(sbi, F2FS_WRITEBACK))
|
||||
f2fs_flush_merged_bios(sbi);
|
||||
|
||||
iput(sbi->node_inode);
|
||||
iput(sbi->meta_inode);
|
||||
|
||||
@ -574,6 +590,8 @@ static void f2fs_put_super(struct super_block *sb)
|
||||
wait_for_completion(&sbi->s_kobj_unregister);
|
||||
|
||||
sb->s_fs_info = NULL;
|
||||
if (sbi->s_chksum_driver)
|
||||
crypto_free_shash(sbi->s_chksum_driver);
|
||||
kfree(sbi->raw_super);
|
||||
kfree(sbi);
|
||||
}
|
||||
@ -766,8 +784,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
||||
bool need_stop_gc = false;
|
||||
bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
|
||||
|
||||
sync_filesystem(sb);
|
||||
|
||||
/*
|
||||
* Save the old mount options in case we
|
||||
* need to restore them.
|
||||
@ -775,6 +791,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
||||
org_mount_opt = sbi->mount_opt;
|
||||
active_logs = sbi->active_logs;
|
||||
|
||||
if (*flags & MS_RDONLY) {
|
||||
set_opt(sbi, FASTBOOT);
|
||||
set_sbi_flag(sbi, SBI_IS_DIRTY);
|
||||
}
|
||||
|
||||
sync_filesystem(sb);
|
||||
|
||||
sbi->mount_opt.opt = 0;
|
||||
default_options(sbi);
|
||||
|
||||
@ -862,6 +885,41 @@ static struct super_operations f2fs_sops = {
|
||||
.remount_fs = f2fs_remount,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
static int f2fs_get_context(struct inode *inode, void *ctx, size_t len)
|
||||
{
|
||||
return f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
|
||||
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
ctx, len, NULL);
|
||||
}
|
||||
|
||||
static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
|
||||
void *fs_data)
|
||||
{
|
||||
return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
|
||||
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
|
||||
ctx, len, fs_data, XATTR_CREATE);
|
||||
}
|
||||
|
||||
static unsigned f2fs_max_namelen(struct inode *inode)
|
||||
{
|
||||
return S_ISLNK(inode->i_mode) ?
|
||||
inode->i_sb->s_blocksize : F2FS_NAME_LEN;
|
||||
}
|
||||
|
||||
static struct fscrypt_operations f2fs_cryptops = {
|
||||
.get_context = f2fs_get_context,
|
||||
.set_context = f2fs_set_context,
|
||||
.is_encrypted = f2fs_encrypted_inode,
|
||||
.empty_dir = f2fs_empty_dir,
|
||||
.max_namelen = f2fs_max_namelen,
|
||||
};
|
||||
#else
|
||||
static struct fscrypt_operations f2fs_cryptops = {
|
||||
.is_encrypted = f2fs_encrypted_inode,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct inode *f2fs_nfs_get_inode(struct super_block *sb,
|
||||
u64 ino, u32 generation)
|
||||
{
|
||||
@ -1074,7 +1132,7 @@ static int sanity_check_raw_super(struct super_block *sb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sanity_check_ckpt(struct f2fs_sb_info *sbi)
|
||||
int sanity_check_ckpt(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
unsigned int total, fsmeta;
|
||||
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
|
||||
@ -1134,14 +1192,15 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
|
||||
|
||||
/*
|
||||
* Read f2fs raw super block.
|
||||
* Because we have two copies of super block, so read the first one at first,
|
||||
* if the first one is invalid, move to read the second one.
|
||||
* Because we have two copies of super block, so read both of them
|
||||
* to get the first valid one. If any one of them is broken, we pass
|
||||
* them recovery flag back to the caller.
|
||||
*/
|
||||
static int read_raw_super_block(struct super_block *sb,
|
||||
struct f2fs_super_block **raw_super,
|
||||
int *valid_super_block, int *recovery)
|
||||
{
|
||||
int block = 0;
|
||||
int block;
|
||||
struct buffer_head *bh;
|
||||
struct f2fs_super_block *super, *buf;
|
||||
int err = 0;
|
||||
@ -1149,50 +1208,48 @@ static int read_raw_super_block(struct super_block *sb,
|
||||
super = kzalloc(sizeof(struct f2fs_super_block), GFP_KERNEL);
|
||||
if (!super)
|
||||
return -ENOMEM;
|
||||
retry:
|
||||
bh = sb_bread(sb, block);
|
||||
if (!bh) {
|
||||
*recovery = 1;
|
||||
f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock",
|
||||
|
||||
for (block = 0; block < 2; block++) {
|
||||
bh = sb_bread(sb, block);
|
||||
if (!bh) {
|
||||
f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock",
|
||||
block + 1);
|
||||
err = -EIO;
|
||||
goto next;
|
||||
}
|
||||
err = -EIO;
|
||||
continue;
|
||||
}
|
||||
|
||||
buf = (struct f2fs_super_block *)(bh->b_data + F2FS_SUPER_OFFSET);
|
||||
buf = (struct f2fs_super_block *)
|
||||
(bh->b_data + F2FS_SUPER_OFFSET);
|
||||
|
||||
/* sanity checking of raw super */
|
||||
if (sanity_check_raw_super(sb, buf)) {
|
||||
/* sanity checking of raw super */
|
||||
if (sanity_check_raw_super(sb, buf)) {
|
||||
f2fs_msg(sb, KERN_ERR,
|
||||
"Can't find valid F2FS filesystem in %dth superblock",
|
||||
block + 1);
|
||||
err = -EINVAL;
|
||||
brelse(bh);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!*raw_super) {
|
||||
memcpy(super, buf, sizeof(*super));
|
||||
*valid_super_block = block;
|
||||
*raw_super = super;
|
||||
}
|
||||
brelse(bh);
|
||||
}
|
||||
|
||||
/* Fail to read any one of the superblocks*/
|
||||
if (err < 0)
|
||||
*recovery = 1;
|
||||
f2fs_msg(sb, KERN_ERR,
|
||||
"Can't find valid F2FS filesystem in %dth superblock",
|
||||
block + 1);
|
||||
err = -EINVAL;
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (!*raw_super) {
|
||||
memcpy(super, buf, sizeof(*super));
|
||||
*valid_super_block = block;
|
||||
*raw_super = super;
|
||||
}
|
||||
brelse(bh);
|
||||
|
||||
next:
|
||||
/* check the validity of the second superblock */
|
||||
if (block == 0) {
|
||||
block++;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/* No valid superblock */
|
||||
if (!*raw_super) {
|
||||
if (!*raw_super)
|
||||
kfree(super);
|
||||
return err;
|
||||
}
|
||||
else
|
||||
err = 0;
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __f2fs_commit_super(struct f2fs_sb_info *sbi, int block)
|
||||
@ -1242,6 +1299,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
bool retry = true, need_fsck = false;
|
||||
char *options = NULL;
|
||||
int recovery, i, valid_super_block;
|
||||
struct curseg_info *seg_i;
|
||||
|
||||
try_onemore:
|
||||
err = -EINVAL;
|
||||
@ -1254,6 +1312,15 @@ try_onemore:
|
||||
if (!sbi)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Load the checksum driver */
|
||||
sbi->s_chksum_driver = crypto_alloc_shash("crc32", 0, 0);
|
||||
if (IS_ERR(sbi->s_chksum_driver)) {
|
||||
f2fs_msg(sb, KERN_ERR, "Cannot load crc32 driver.");
|
||||
err = PTR_ERR(sbi->s_chksum_driver);
|
||||
sbi->s_chksum_driver = NULL;
|
||||
goto free_sbi;
|
||||
}
|
||||
|
||||
/* set a block size */
|
||||
if (unlikely(!sb_set_blocksize(sb, F2FS_BLKSIZE))) {
|
||||
f2fs_msg(sb, KERN_ERR, "unable to set blocksize");
|
||||
@ -1285,6 +1352,7 @@ try_onemore:
|
||||
get_random_bytes(&sbi->s_next_generation, sizeof(u32));
|
||||
|
||||
sb->s_op = &f2fs_sops;
|
||||
sb->s_cop = &f2fs_cryptops;
|
||||
sb->s_xattr = f2fs_xattr_handlers;
|
||||
sb->s_export_op = &f2fs_export_ops;
|
||||
sb->s_magic = F2FS_SUPER_MAGIC;
|
||||
@ -1333,13 +1401,6 @@ try_onemore:
|
||||
goto free_meta_inode;
|
||||
}
|
||||
|
||||
/* sanity checking of checkpoint */
|
||||
err = -EINVAL;
|
||||
if (sanity_check_ckpt(sbi)) {
|
||||
f2fs_msg(sb, KERN_ERR, "Invalid F2FS checkpoint");
|
||||
goto free_cp;
|
||||
}
|
||||
|
||||
sbi->total_valid_node_count =
|
||||
le32_to_cpu(sbi->ckpt->valid_node_count);
|
||||
sbi->total_valid_inode_count =
|
||||
@ -1372,6 +1433,17 @@ try_onemore:
|
||||
goto free_nm;
|
||||
}
|
||||
|
||||
/* For write statistics */
|
||||
if (sb->s_bdev->bd_part)
|
||||
sbi->sectors_written_start =
|
||||
(u64)part_stat_read(sb->s_bdev->bd_part, sectors[1]);
|
||||
|
||||
/* Read accumulated write IO statistics if exists */
|
||||
seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE);
|
||||
if (__exist_node_summaries(sbi))
|
||||
sbi->kbytes_written =
|
||||
le64_to_cpu(seg_i->sum_blk->journal.info.kbytes_written);
|
||||
|
||||
build_gc_manager(sbi);
|
||||
|
||||
/* get an inode for node space */
|
||||
@ -1466,8 +1538,10 @@ try_onemore:
|
||||
|
||||
/* recover broken superblock */
|
||||
if (recovery && !f2fs_readonly(sb) && !bdev_read_only(sb->s_bdev)) {
|
||||
f2fs_msg(sb, KERN_INFO, "Recover invalid superblock");
|
||||
f2fs_commit_super(sbi, true);
|
||||
err = f2fs_commit_super(sbi, true);
|
||||
f2fs_msg(sb, KERN_INFO,
|
||||
"Try to recover %dth superblock, ret: %ld",
|
||||
sbi->valid_super_block ? 1 : 2, err);
|
||||
}
|
||||
|
||||
f2fs_update_time(sbi, CP_TIME);
|
||||
@ -1496,7 +1570,6 @@ free_nm:
|
||||
destroy_node_manager(sbi);
|
||||
free_sm:
|
||||
destroy_segment_manager(sbi);
|
||||
free_cp:
|
||||
kfree(sbi->ckpt);
|
||||
free_meta_inode:
|
||||
make_bad_inode(sbi->meta_inode);
|
||||
@ -1506,6 +1579,8 @@ free_options:
|
||||
free_sb_buf:
|
||||
kfree(raw_super);
|
||||
free_sbi:
|
||||
if (sbi->s_chksum_driver)
|
||||
crypto_free_shash(sbi->s_chksum_driver);
|
||||
kfree(sbi);
|
||||
|
||||
/* give only one another chance */
|
||||
@ -1585,13 +1660,9 @@ static int __init init_f2fs_fs(void)
|
||||
err = -ENOMEM;
|
||||
goto free_extent_cache;
|
||||
}
|
||||
err = f2fs_init_crypto();
|
||||
if (err)
|
||||
goto free_kset;
|
||||
|
||||
err = register_shrinker(&f2fs_shrinker_info);
|
||||
if (err)
|
||||
goto free_crypto;
|
||||
goto free_kset;
|
||||
|
||||
err = register_filesystem(&f2fs_fs_type);
|
||||
if (err)
|
||||
@ -1606,8 +1677,6 @@ free_filesystem:
|
||||
unregister_filesystem(&f2fs_fs_type);
|
||||
free_shrinker:
|
||||
unregister_shrinker(&f2fs_shrinker_info);
|
||||
free_crypto:
|
||||
f2fs_exit_crypto();
|
||||
free_kset:
|
||||
kset_unregister(f2fs_kset);
|
||||
free_extent_cache:
|
||||
@ -1630,7 +1699,6 @@ static void __exit exit_f2fs_fs(void)
|
||||
f2fs_destroy_root_stats();
|
||||
unregister_shrinker(&f2fs_shrinker_info);
|
||||
unregister_filesystem(&f2fs_fs_type);
|
||||
f2fs_exit_crypto();
|
||||
destroy_extent_cache();
|
||||
destroy_checkpoint_caches();
|
||||
destroy_segment_manager_caches();
|
||||
|
@ -29,7 +29,8 @@ static inline void __print_last_io(void)
|
||||
last_io.major, last_io.minor,
|
||||
last_io.pid, "----------------",
|
||||
last_io.type,
|
||||
last_io.fio.rw, last_io.fio.blk_addr,
|
||||
last_io.fio.rw,
|
||||
last_io.fio.new_blkaddr,
|
||||
last_io.len);
|
||||
memset(&last_io, 0, sizeof(last_io));
|
||||
}
|
||||
@ -101,7 +102,8 @@ void f2fs_trace_ios(struct f2fs_io_info *fio, int flush)
|
||||
last_io.pid == pid &&
|
||||
last_io.type == __file_type(inode, pid) &&
|
||||
last_io.fio.rw == fio->rw &&
|
||||
last_io.fio.blk_addr + last_io.len == fio->blk_addr) {
|
||||
last_io.fio.new_blkaddr + last_io.len ==
|
||||
fio->new_blkaddr) {
|
||||
last_io.len++;
|
||||
return;
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
||||
|
||||
if (ipage) {
|
||||
inline_addr = inline_xattr_addr(ipage);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
} else {
|
||||
page = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(page)) {
|
||||
@ -308,7 +308,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
||||
return PTR_ERR(page);
|
||||
}
|
||||
inline_addr = inline_xattr_addr(page);
|
||||
f2fs_wait_on_page_writeback(page, NODE);
|
||||
f2fs_wait_on_page_writeback(page, NODE, true);
|
||||
}
|
||||
memcpy(inline_addr, txattr_addr, inline_size);
|
||||
f2fs_put_page(page, 1);
|
||||
@ -329,7 +329,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
||||
return PTR_ERR(xpage);
|
||||
}
|
||||
f2fs_bug_on(sbi, new_nid);
|
||||
f2fs_wait_on_page_writeback(xpage, NODE);
|
||||
f2fs_wait_on_page_writeback(xpage, NODE, true);
|
||||
} else {
|
||||
struct dnode_of_data dn;
|
||||
set_new_dnode(&dn, inode, NULL, NULL, new_nid);
|
||||
|
@ -126,7 +126,8 @@ extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t);
|
||||
|
||||
#define f2fs_xattr_handlers NULL
|
||||
static inline int f2fs_setxattr(struct inode *inode, int index,
|
||||
const char *name, const void *value, size_t size, int flags)
|
||||
const char *name, const void *value, size_t size,
|
||||
struct page *page, int flags)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -228,6 +228,8 @@ struct dentry_operations {
|
||||
#define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */
|
||||
#define DCACHE_OP_SELECT_INODE 0x02000000 /* Unioned entry: dcache op selects inode */
|
||||
|
||||
#define DCACHE_ENCRYPTED_WITH_KEY 0x04000000 /* dir is encrypted with a valid key */
|
||||
|
||||
extern seqlock_t rename_lock;
|
||||
|
||||
/*
|
||||
|
@ -21,7 +21,7 @@
|
||||
#define F2FS_BLKSIZE 4096 /* support only 4KB block */
|
||||
#define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */
|
||||
#define F2FS_MAX_EXTENSION 64 /* # of extension entries */
|
||||
#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) / F2FS_BLKSIZE)
|
||||
#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) >> F2FS_BLKSIZE_BITS)
|
||||
|
||||
#define NULL_ADDR ((block_t)0) /* used as block_t addresses */
|
||||
#define NEW_ADDR ((block_t)-1) /* used as block_t addresses */
|
||||
@ -170,12 +170,12 @@ struct f2fs_extent {
|
||||
#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */
|
||||
#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
|
||||
#define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */
|
||||
#define ADDRS_PER_INODE(fi) addrs_per_inode(fi)
|
||||
#define ADDRS_PER_INODE(inode) addrs_per_inode(inode)
|
||||
#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
|
||||
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
|
||||
|
||||
#define ADDRS_PER_PAGE(page, fi) \
|
||||
(IS_INODE(page) ? ADDRS_PER_INODE(fi) : ADDRS_PER_BLOCK)
|
||||
#define ADDRS_PER_PAGE(page, inode) \
|
||||
(IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK)
|
||||
|
||||
#define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1)
|
||||
#define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2)
|
||||
@ -345,7 +345,7 @@ struct f2fs_summary {
|
||||
|
||||
struct summary_footer {
|
||||
unsigned char entry_type; /* SUM_TYPE_XXX */
|
||||
__u32 check_sum; /* summary checksum */
|
||||
__le32 check_sum; /* summary checksum */
|
||||
} __packed;
|
||||
|
||||
#define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\
|
||||
@ -358,6 +358,12 @@ struct summary_footer {
|
||||
sizeof(struct sit_journal_entry))
|
||||
#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\
|
||||
sizeof(struct sit_journal_entry))
|
||||
|
||||
/* Reserved area should make size of f2fs_extra_info equals to
|
||||
* that of nat_journal and sit_journal.
|
||||
*/
|
||||
#define EXTRA_INFO_RESERVED (SUM_JOURNAL_SIZE - 2 - 8)
|
||||
|
||||
/*
|
||||
* frequently updated NAT/SIT entries can be stored in the spare area in
|
||||
* summary blocks
|
||||
@ -387,18 +393,28 @@ struct sit_journal {
|
||||
__u8 reserved[SIT_JOURNAL_RESERVED];
|
||||
} __packed;
|
||||
|
||||
/* 4KB-sized summary block structure */
|
||||
struct f2fs_summary_block {
|
||||
struct f2fs_summary entries[ENTRIES_IN_SUM];
|
||||
struct f2fs_extra_info {
|
||||
__le64 kbytes_written;
|
||||
__u8 reserved[EXTRA_INFO_RESERVED];
|
||||
} __packed;
|
||||
|
||||
struct f2fs_journal {
|
||||
union {
|
||||
__le16 n_nats;
|
||||
__le16 n_sits;
|
||||
};
|
||||
/* spare area is used by NAT or SIT journals */
|
||||
/* spare area is used by NAT or SIT journals or extra info */
|
||||
union {
|
||||
struct nat_journal nat_j;
|
||||
struct sit_journal sit_j;
|
||||
struct f2fs_extra_info info;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* 4KB-sized summary block structure */
|
||||
struct f2fs_summary_block {
|
||||
struct f2fs_summary entries[ENTRIES_IN_SUM];
|
||||
struct f2fs_journal journal;
|
||||
struct summary_footer footer;
|
||||
} __packed;
|
||||
|
||||
|
@ -53,6 +53,8 @@ struct swap_info_struct;
|
||||
struct seq_file;
|
||||
struct workqueue_struct;
|
||||
struct iov_iter;
|
||||
struct fscrypt_info;
|
||||
struct fscrypt_operations;
|
||||
|
||||
extern void __init inode_init(void);
|
||||
extern void __init inode_init_early(void);
|
||||
@ -679,6 +681,10 @@ struct inode {
|
||||
struct hlist_head i_fsnotify_marks;
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
struct fscrypt_info *i_crypt_info;
|
||||
#endif
|
||||
|
||||
void *i_private; /* fs or device private pointer */
|
||||
};
|
||||
|
||||
@ -1324,6 +1330,8 @@ struct super_block {
|
||||
#endif
|
||||
const struct xattr_handler **s_xattr;
|
||||
|
||||
const struct fscrypt_operations *s_cop;
|
||||
|
||||
struct hlist_bl_head s_anon; /* anonymous dentries for (nfs) exporting */
|
||||
struct list_head s_mounts; /* list of mounts; _not_ for fs use */
|
||||
struct block_device *s_bdev;
|
||||
|
434
include/linux/fscrypto.h
Normal file
434
include/linux/fscrypto.h
Normal file
@ -0,0 +1,434 @@
|
||||
/*
|
||||
* General per-file encryption definition
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
*
|
||||
* Written by Michael Halcrow, 2015.
|
||||
* Modified by Jaegeuk Kim, 2015.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FSCRYPTO_H
|
||||
#define _LINUX_FSCRYPTO_H
|
||||
|
||||
#include <linux/key.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <crypto/skcipher.h>
|
||||
#include <uapi/linux/fs.h>
|
||||
|
||||
#define FS_KEY_DERIVATION_NONCE_SIZE 16
|
||||
#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1
|
||||
|
||||
#define FS_POLICY_FLAGS_PAD_4 0x00
|
||||
#define FS_POLICY_FLAGS_PAD_8 0x01
|
||||
#define FS_POLICY_FLAGS_PAD_16 0x02
|
||||
#define FS_POLICY_FLAGS_PAD_32 0x03
|
||||
#define FS_POLICY_FLAGS_PAD_MASK 0x03
|
||||
#define FS_POLICY_FLAGS_VALID 0x03
|
||||
|
||||
/* Encryption algorithms */
|
||||
#define FS_ENCRYPTION_MODE_INVALID 0
|
||||
#define FS_ENCRYPTION_MODE_AES_256_XTS 1
|
||||
#define FS_ENCRYPTION_MODE_AES_256_GCM 2
|
||||
#define FS_ENCRYPTION_MODE_AES_256_CBC 3
|
||||
#define FS_ENCRYPTION_MODE_AES_256_CTS 4
|
||||
|
||||
/**
|
||||
* Encryption context for inode
|
||||
*
|
||||
* Protector format:
|
||||
* 1 byte: Protector format (1 = this version)
|
||||
* 1 byte: File contents encryption mode
|
||||
* 1 byte: File names encryption mode
|
||||
* 1 byte: Flags
|
||||
* 8 bytes: Master Key descriptor
|
||||
* 16 bytes: Encryption Key derivation nonce
|
||||
*/
|
||||
struct fscrypt_context {
|
||||
u8 format;
|
||||
u8 contents_encryption_mode;
|
||||
u8 filenames_encryption_mode;
|
||||
u8 flags;
|
||||
u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
|
||||
u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
|
||||
} __packed;
|
||||
|
||||
/* Encryption parameters */
|
||||
#define FS_XTS_TWEAK_SIZE 16
|
||||
#define FS_AES_128_ECB_KEY_SIZE 16
|
||||
#define FS_AES_256_GCM_KEY_SIZE 32
|
||||
#define FS_AES_256_CBC_KEY_SIZE 32
|
||||
#define FS_AES_256_CTS_KEY_SIZE 32
|
||||
#define FS_AES_256_XTS_KEY_SIZE 64
|
||||
#define FS_MAX_KEY_SIZE 64
|
||||
|
||||
#define FS_KEY_DESC_PREFIX "fscrypt:"
|
||||
#define FS_KEY_DESC_PREFIX_SIZE 8
|
||||
|
||||
/* This is passed in from userspace into the kernel keyring */
|
||||
struct fscrypt_key {
|
||||
u32 mode;
|
||||
u8 raw[FS_MAX_KEY_SIZE];
|
||||
u32 size;
|
||||
} __packed;
|
||||
|
||||
struct fscrypt_info {
|
||||
u8 ci_data_mode;
|
||||
u8 ci_filename_mode;
|
||||
u8 ci_flags;
|
||||
struct crypto_skcipher *ci_ctfm;
|
||||
struct key *ci_keyring_key;
|
||||
u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE];
|
||||
};
|
||||
|
||||
#define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
|
||||
#define FS_WRITE_PATH_FL 0x00000002
|
||||
|
||||
struct fscrypt_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 */
|
||||
};
|
||||
u8 flags; /* Flags */
|
||||
u8 mode; /* Encryption mode for tfm */
|
||||
};
|
||||
|
||||
struct fscrypt_completion_result {
|
||||
struct completion completion;
|
||||
int res;
|
||||
};
|
||||
|
||||
#define DECLARE_FS_COMPLETION_RESULT(ecr) \
|
||||
struct fscrypt_completion_result ecr = { \
|
||||
COMPLETION_INITIALIZER((ecr).completion), 0 }
|
||||
|
||||
static inline int fscrypt_key_size(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case FS_ENCRYPTION_MODE_AES_256_XTS:
|
||||
return FS_AES_256_XTS_KEY_SIZE;
|
||||
case FS_ENCRYPTION_MODE_AES_256_GCM:
|
||||
return FS_AES_256_GCM_KEY_SIZE;
|
||||
case FS_ENCRYPTION_MODE_AES_256_CBC:
|
||||
return FS_AES_256_CBC_KEY_SIZE;
|
||||
case FS_ENCRYPTION_MODE_AES_256_CTS:
|
||||
return FS_AES_256_CTS_KEY_SIZE;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FS_FNAME_NUM_SCATTER_ENTRIES 4
|
||||
#define FS_CRYPTO_BLOCK_SIZE 16
|
||||
#define FS_FNAME_CRYPTO_DIGEST_SIZE 32
|
||||
|
||||
/**
|
||||
* For encrypted symlinks, the ciphertext length is stored at the beginning
|
||||
* of the string in little-endian format.
|
||||
*/
|
||||
struct fscrypt_symlink_data {
|
||||
__le16 len;
|
||||
char encrypted_path[1];
|
||||
} __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 fscrypt_symlink_data_len(u32 l)
|
||||
{
|
||||
if (l < FS_CRYPTO_BLOCK_SIZE)
|
||||
l = FS_CRYPTO_BLOCK_SIZE;
|
||||
return (l + sizeof(struct fscrypt_symlink_data) - 1);
|
||||
}
|
||||
|
||||
struct fscrypt_str {
|
||||
unsigned char *name;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
struct fscrypt_name {
|
||||
const struct qstr *usr_fname;
|
||||
struct fscrypt_str disk_name;
|
||||
u32 hash;
|
||||
u32 minor_hash;
|
||||
struct fscrypt_str crypto_buf;
|
||||
};
|
||||
|
||||
#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)
|
||||
|
||||
/*
|
||||
* crypto opertions for filesystems
|
||||
*/
|
||||
struct fscrypt_operations {
|
||||
int (*get_context)(struct inode *, void *, size_t);
|
||||
int (*prepare_context)(struct inode *);
|
||||
int (*set_context)(struct inode *, const void *, size_t, void *);
|
||||
int (*dummy_context)(struct inode *);
|
||||
bool (*is_encrypted)(struct inode *);
|
||||
bool (*empty_dir)(struct inode *);
|
||||
unsigned (*max_namelen)(struct inode *);
|
||||
};
|
||||
|
||||
static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
|
||||
{
|
||||
if (inode->i_sb->s_cop->dummy_context &&
|
||||
inode->i_sb->s_cop->dummy_context(inode))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool fscrypt_valid_contents_enc_mode(u32 mode)
|
||||
{
|
||||
return (mode == FS_ENCRYPTION_MODE_AES_256_XTS);
|
||||
}
|
||||
|
||||
static inline bool fscrypt_valid_filenames_enc_mode(u32 mode)
|
||||
{
|
||||
return (mode == FS_ENCRYPTION_MODE_AES_256_CTS);
|
||||
}
|
||||
|
||||
static inline u32 fscrypt_validate_encryption_key_size(u32 mode, u32 size)
|
||||
{
|
||||
if (size == fscrypt_key_size(mode))
|
||||
return size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
|
||||
{
|
||||
if (str->len == 1 && str->name[0] == '.')
|
||||
return true;
|
||||
|
||||
if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct page *fscrypt_control_page(struct page *page)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
return ((struct fscrypt_ctx *)page_private(page))->w.control_page;
|
||||
#else
|
||||
WARN_ON_ONCE(1);
|
||||
return ERR_PTR(-EINVAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int fscrypt_has_encryption_key(struct inode *inode)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
return (inode->i_crypt_info != NULL);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void fscrypt_set_encrypted_dentry(struct dentry *dentry)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
extern const struct dentry_operations fscrypt_d_ops;
|
||||
#endif
|
||||
|
||||
static inline void fscrypt_set_d_op(struct dentry *dentry)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
d_set_d_op(dentry, &fscrypt_d_ops);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||
/* crypto.c */
|
||||
extern struct kmem_cache *fscrypt_info_cachep;
|
||||
int fscrypt_initialize(void);
|
||||
|
||||
extern struct fscrypt_ctx *fscrypt_get_ctx(struct inode *);
|
||||
extern void fscrypt_release_ctx(struct fscrypt_ctx *);
|
||||
extern struct page *fscrypt_encrypt_page(struct inode *, struct page *);
|
||||
extern int fscrypt_decrypt_page(struct page *);
|
||||
extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *);
|
||||
extern void fscrypt_pullback_bio_page(struct page **, bool);
|
||||
extern void fscrypt_restore_control_page(struct page *);
|
||||
extern int fscrypt_zeroout_range(struct inode *, pgoff_t, sector_t,
|
||||
unsigned int);
|
||||
/* policy.c */
|
||||
extern int fscrypt_process_policy(struct inode *,
|
||||
const struct fscrypt_policy *);
|
||||
extern int fscrypt_get_policy(struct inode *, struct fscrypt_policy *);
|
||||
extern int fscrypt_has_permitted_context(struct inode *, struct inode *);
|
||||
extern int fscrypt_inherit_context(struct inode *, struct inode *,
|
||||
void *, bool);
|
||||
/* keyinfo.c */
|
||||
extern int get_crypt_info(struct inode *);
|
||||
extern int fscrypt_get_encryption_info(struct inode *);
|
||||
extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *);
|
||||
|
||||
/* fname.c */
|
||||
extern int fscrypt_setup_filename(struct inode *, const struct qstr *,
|
||||
int lookup, struct fscrypt_name *);
|
||||
extern void fscrypt_free_filename(struct fscrypt_name *);
|
||||
extern u32 fscrypt_fname_encrypted_size(struct inode *, u32);
|
||||
extern int fscrypt_fname_alloc_buffer(struct inode *, u32,
|
||||
struct fscrypt_str *);
|
||||
extern void fscrypt_fname_free_buffer(struct fscrypt_str *);
|
||||
extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32,
|
||||
const struct fscrypt_str *, struct fscrypt_str *);
|
||||
extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *,
|
||||
struct fscrypt_str *);
|
||||
#endif
|
||||
|
||||
/* crypto.c */
|
||||
static inline struct fscrypt_ctx *fscrypt_notsupp_get_ctx(struct inode *i)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_release_ctx(struct fscrypt_ctx *c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline struct page *fscrypt_notsupp_encrypt_page(struct inode *i,
|
||||
struct page *p)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_decrypt_page(struct page *p)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_decrypt_bio_pages(struct fscrypt_ctx *c,
|
||||
struct bio *b)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_pullback_bio_page(struct page **p, bool b)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_restore_control_page(struct page *p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_zeroout_range(struct inode *i, pgoff_t p,
|
||||
sector_t s, unsigned int f)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* policy.c */
|
||||
static inline int fscrypt_notsupp_process_policy(struct inode *i,
|
||||
const struct fscrypt_policy *p)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_get_policy(struct inode *i,
|
||||
struct fscrypt_policy *p)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_has_permitted_context(struct inode *p,
|
||||
struct inode *i)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_inherit_context(struct inode *p,
|
||||
struct inode *i, void *v, bool b)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* keyinfo.c */
|
||||
static inline int fscrypt_notsupp_get_encryption_info(struct inode *i)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_put_encryption_info(struct inode *i,
|
||||
struct fscrypt_info *f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* fname.c */
|
||||
static inline int fscrypt_notsupp_setup_filename(struct inode *dir,
|
||||
const struct qstr *iname,
|
||||
int lookup, struct fscrypt_name *fname)
|
||||
{
|
||||
if (dir->i_sb->s_cop->is_encrypted(dir))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
memset(fname, 0, sizeof(struct fscrypt_name));
|
||||
fname->usr_fname = iname;
|
||||
fname->disk_name.name = (unsigned char *)iname->name;
|
||||
fname->disk_name.len = iname->len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_free_filename(struct fscrypt_name *fname)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline u32 fscrypt_notsupp_fname_encrypted_size(struct inode *i, u32 s)
|
||||
{
|
||||
/* never happens */
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_fname_alloc_buffer(struct inode *inode,
|
||||
u32 ilen, struct fscrypt_str *crypto_str)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void fscrypt_notsupp_fname_free_buffer(struct fscrypt_str *c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_fname_disk_to_usr(struct inode *inode,
|
||||
u32 hash, u32 minor_hash,
|
||||
const struct fscrypt_str *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fscrypt_notsupp_fname_usr_to_disk(struct inode *inode,
|
||||
const struct qstr *iname,
|
||||
struct fscrypt_str *oname)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif /* _LINUX_FSCRYPTO_H */
|
@ -52,6 +52,7 @@ TRACE_DEFINE_ENUM(CP_DISCARD);
|
||||
{ META_FLUSH, "META_FLUSH" }, \
|
||||
{ INMEM, "INMEM" }, \
|
||||
{ INMEM_DROP, "INMEM_DROP" }, \
|
||||
{ INMEM_REVOKE, "INMEM_REVOKE" }, \
|
||||
{ IPU, "IN-PLACE" }, \
|
||||
{ OPU, "OUT-OF-PLACE" })
|
||||
|
||||
@ -727,7 +728,8 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio,
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(pgoff_t, index)
|
||||
__field(block_t, blkaddr)
|
||||
__field(block_t, old_blkaddr)
|
||||
__field(block_t, new_blkaddr)
|
||||
__field(int, rw)
|
||||
__field(int, type)
|
||||
),
|
||||
@ -736,16 +738,18 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio,
|
||||
__entry->dev = page->mapping->host->i_sb->s_dev;
|
||||
__entry->ino = page->mapping->host->i_ino;
|
||||
__entry->index = page->index;
|
||||
__entry->blkaddr = fio->blk_addr;
|
||||
__entry->old_blkaddr = fio->old_blkaddr;
|
||||
__entry->new_blkaddr = fio->new_blkaddr;
|
||||
__entry->rw = fio->rw;
|
||||
__entry->type = fio->type;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, "
|
||||
"blkaddr = 0x%llx, rw = %s%s, type = %s",
|
||||
"oldaddr = 0x%llx, newaddr = 0x%llx rw = %s%s, type = %s",
|
||||
show_dev_ino(__entry),
|
||||
(unsigned long)__entry->index,
|
||||
(unsigned long long)__entry->blkaddr,
|
||||
(unsigned long long)__entry->old_blkaddr,
|
||||
(unsigned long long)__entry->new_blkaddr,
|
||||
show_bio_type(__entry->rw),
|
||||
show_block_type(__entry->type))
|
||||
);
|
||||
|
@ -246,6 +246,24 @@ struct fsxattr {
|
||||
#define FS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr)
|
||||
#define FS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr)
|
||||
|
||||
/*
|
||||
* File system encryption support
|
||||
*/
|
||||
/* Policy provided via an ioctl on the topmost directory */
|
||||
#define FS_KEY_DESCRIPTOR_SIZE 8
|
||||
|
||||
struct fscrypt_policy {
|
||||
__u8 version;
|
||||
__u8 contents_encryption_mode;
|
||||
__u8 filenames_encryption_mode;
|
||||
__u8 flags;
|
||||
__u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
|
||||
} __packed;
|
||||
|
||||
#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
|
||||
#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16])
|
||||
#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
|
||||
|
||||
/*
|
||||
* Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user