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>
|
Contact: "Chao Yu" <chao2.yu@samsung.com>
|
||||||
Description:
|
Description:
|
||||||
Controls the count of nid pages to be readaheaded.
|
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.
|
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/notify/Kconfig"
|
||||||
|
|
||||||
source "fs/quota/Kconfig"
|
source "fs/quota/Kconfig"
|
||||||
|
@ -30,6 +30,7 @@ obj-$(CONFIG_EVENTFD) += eventfd.o
|
|||||||
obj-$(CONFIG_USERFAULTFD) += userfaultfd.o
|
obj-$(CONFIG_USERFAULTFD) += userfaultfd.o
|
||||||
obj-$(CONFIG_AIO) += aio.o
|
obj-$(CONFIG_AIO) += aio.o
|
||||||
obj-$(CONFIG_FS_DAX) += dax.o
|
obj-$(CONFIG_FS_DAX) += dax.o
|
||||||
|
obj-$(CONFIG_FS_ENCRYPTION) += crypto/
|
||||||
obj-$(CONFIG_FILE_LOCKING) += locks.o
|
obj-$(CONFIG_FILE_LOCKING) += locks.o
|
||||||
obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o
|
obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o
|
||||||
obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.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
|
* This contains functions for filename crypto management
|
||||||
*
|
|
||||||
* Copied from linux/fs/ext4/crypto.c
|
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015, Google, Inc.
|
* Copyright (C) 2015, Google, Inc.
|
||||||
* Copyright (C) 2015, Motorola Mobility
|
* Copyright (C) 2015, Motorola Mobility
|
||||||
*
|
*
|
||||||
* This contains functions for filename crypto management in f2fs
|
|
||||||
*
|
|
||||||
* Written by Uday Savagaonkar, 2014.
|
* Written by Uday Savagaonkar, 2014.
|
||||||
*
|
* Modified by Jaegeuk Kim, 2015.
|
||||||
* Adjust f2fs dentry structure
|
|
||||||
* Jaegeuk Kim, 2015.
|
|
||||||
*
|
*
|
||||||
* This has not yet undergone a rigorous security audit.
|
* This has not yet undergone a rigorous security audit.
|
||||||
*/
|
*/
|
||||||
#include <crypto/skcipher.h>
|
|
||||||
#include <keys/encrypted-type.h>
|
#include <keys/encrypted-type.h>
|
||||||
#include <keys/user-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/scatterlist.h>
|
||||||
#include <linux/spinlock_types.h>
|
|
||||||
#include <linux/f2fs_fs.h>
|
|
||||||
#include <linux/ratelimit.h>
|
#include <linux/ratelimit.h>
|
||||||
|
#include <linux/fscrypto.h>
|
||||||
|
|
||||||
#include "f2fs.h"
|
static u32 size_round_up(size_t size, size_t blksize)
|
||||||
#include "f2fs_crypto.h"
|
{
|
||||||
#include "xattr.h"
|
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)
|
if (res == -EINPROGRESS)
|
||||||
return;
|
return;
|
||||||
@ -46,45 +34,35 @@ static void f2fs_dir_crypt_complete(struct crypto_async_request *req, int res)
|
|||||||
complete(&ecr->completion);
|
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
|
* This function encrypts the input filename, and returns the length of the
|
||||||
* ciphertext. Errors are returned as negative numbers. We trust the caller to
|
* ciphertext. Errors are returned as negative numbers. We trust the caller to
|
||||||
* allocate sufficient memory to oname string.
|
* allocate sufficient memory to oname string.
|
||||||
*/
|
*/
|
||||||
static int f2fs_fname_encrypt(struct inode *inode,
|
static int fname_encrypt(struct inode *inode,
|
||||||
const struct qstr *iname, struct f2fs_str *oname)
|
const struct qstr *iname, struct fscrypt_str *oname)
|
||||||
{
|
{
|
||||||
u32 ciphertext_len;
|
u32 ciphertext_len;
|
||||||
struct skcipher_request *req = NULL;
|
struct skcipher_request *req = NULL;
|
||||||
DECLARE_F2FS_COMPLETION_RESULT(ecr);
|
DECLARE_FS_COMPLETION_RESULT(ecr);
|
||||||
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;
|
struct crypto_skcipher *tfm = ci->ci_ctfm;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
char iv[F2FS_CRYPTO_BLOCK_SIZE];
|
char iv[FS_CRYPTO_BLOCK_SIZE];
|
||||||
struct scatterlist src_sg, dst_sg;
|
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;
|
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)
|
if (iname->len <= 0 || iname->len > lim)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
ciphertext_len = (iname->len < F2FS_CRYPTO_BLOCK_SIZE) ?
|
ciphertext_len = (iname->len < FS_CRYPTO_BLOCK_SIZE) ?
|
||||||
F2FS_CRYPTO_BLOCK_SIZE : iname->len;
|
FS_CRYPTO_BLOCK_SIZE : iname->len;
|
||||||
ciphertext_len = f2fs_fname_crypto_round_up(ciphertext_len, padding);
|
ciphertext_len = size_round_up(ciphertext_len, padding);
|
||||||
ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len;
|
ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len;
|
||||||
|
|
||||||
if (ciphertext_len <= sizeof(buf)) {
|
if (ciphertext_len <= sizeof(buf)) {
|
||||||
@ -106,7 +84,7 @@ static int f2fs_fname_encrypt(struct inode *inode,
|
|||||||
}
|
}
|
||||||
skcipher_request_set_callback(req,
|
skcipher_request_set_callback(req,
|
||||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||||
f2fs_dir_crypt_complete, &ecr);
|
dir_crypt_complete, &ecr);
|
||||||
|
|
||||||
/* Copy the input */
|
/* Copy the input */
|
||||||
memcpy(workbuf, iname->name, iname->len);
|
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);
|
memset(workbuf + iname->len, 0, ciphertext_len - iname->len);
|
||||||
|
|
||||||
/* Initialize IV */
|
/* Initialize IV */
|
||||||
memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE);
|
memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
|
||||||
|
|
||||||
/* Create encryption request */
|
/* Create encryption request */
|
||||||
sg_init_one(&src_sg, workbuf, ciphertext_len);
|
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);
|
skcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv);
|
||||||
res = crypto_skcipher_encrypt(req);
|
res = crypto_skcipher_encrypt(req);
|
||||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||||
BUG_ON(req->base.data != &ecr);
|
|
||||||
wait_for_completion(&ecr.completion);
|
wait_for_completion(&ecr.completion);
|
||||||
res = ecr.res;
|
res = ecr.res;
|
||||||
}
|
}
|
||||||
kfree(alloc_buf);
|
kfree(alloc_buf);
|
||||||
skcipher_request_free(req);
|
skcipher_request_free(req);
|
||||||
if (res < 0) {
|
if (res < 0)
|
||||||
printk_ratelimited(KERN_ERR
|
printk_ratelimited(KERN_ERR
|
||||||
"%s: Error (error code %d)\n", __func__, res);
|
"%s: Error (error code %d)\n", __func__, res);
|
||||||
}
|
|
||||||
oname->len = ciphertext_len;
|
oname->len = ciphertext_len;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* f2fs_fname_decrypt()
|
* fname_decrypt()
|
||||||
* This function decrypts the input filename, and returns
|
* This function decrypts the input filename, and returns
|
||||||
* the length of the plaintext.
|
* the length of the plaintext.
|
||||||
* Errors are returned as negative numbers.
|
* Errors are returned as negative numbers.
|
||||||
* We trust the caller to allocate sufficient memory to oname string.
|
* We trust the caller to allocate sufficient memory to oname string.
|
||||||
*/
|
*/
|
||||||
static int f2fs_fname_decrypt(struct inode *inode,
|
static int fname_decrypt(struct inode *inode,
|
||||||
const struct f2fs_str *iname, struct f2fs_str *oname)
|
const struct fscrypt_str *iname,
|
||||||
|
struct fscrypt_str *oname)
|
||||||
{
|
{
|
||||||
struct skcipher_request *req = NULL;
|
struct skcipher_request *req = NULL;
|
||||||
DECLARE_F2FS_COMPLETION_RESULT(ecr);
|
DECLARE_FS_COMPLETION_RESULT(ecr);
|
||||||
struct scatterlist src_sg, dst_sg;
|
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;
|
struct crypto_skcipher *tfm = ci->ci_ctfm;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
char iv[F2FS_CRYPTO_BLOCK_SIZE];
|
char iv[FS_CRYPTO_BLOCK_SIZE];
|
||||||
unsigned lim = max_name_len(inode);
|
unsigned lim;
|
||||||
|
|
||||||
|
lim = inode->i_sb->s_cop->max_namelen(inode);
|
||||||
if (iname->len <= 0 || iname->len > lim)
|
if (iname->len <= 0 || iname->len > lim)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
@ -167,10 +146,10 @@ static int f2fs_fname_decrypt(struct inode *inode,
|
|||||||
}
|
}
|
||||||
skcipher_request_set_callback(req,
|
skcipher_request_set_callback(req,
|
||||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||||
f2fs_dir_crypt_complete, &ecr);
|
dir_crypt_complete, &ecr);
|
||||||
|
|
||||||
/* Initialize IV */
|
/* Initialize IV */
|
||||||
memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE);
|
memset(iv, 0, FS_CRYPTO_BLOCK_SIZE);
|
||||||
|
|
||||||
/* Create decryption request */
|
/* Create decryption request */
|
||||||
sg_init_one(&src_sg, iname->name, iname->len);
|
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);
|
skcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv);
|
||||||
res = crypto_skcipher_decrypt(req);
|
res = crypto_skcipher_decrypt(req);
|
||||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||||
BUG_ON(req->base.data != &ecr);
|
|
||||||
wait_for_completion(&ecr.completion);
|
wait_for_completion(&ecr.completion);
|
||||||
res = ecr.res;
|
res = ecr.res;
|
||||||
}
|
}
|
||||||
skcipher_request_free(req);
|
skcipher_request_free(req);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
printk_ratelimited(KERN_ERR
|
printk_ratelimited(KERN_ERR
|
||||||
"%s: Error in f2fs_fname_decrypt (error code %d)\n",
|
"%s: Error (error code %d)\n", __func__, res);
|
||||||
__func__, res);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +175,7 @@ static const char *lookup_table =
|
|||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* f2fs_fname_encode_digest() -
|
* digest_encode() -
|
||||||
*
|
*
|
||||||
* Encodes the input digest using characters from the set [a-zA-Z0-9_+].
|
* 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.
|
* 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;
|
return cp - dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
u32 fscrypt_fname_encrypted_size(struct inode *inode, u32 ilen)
|
||||||
* f2fs_fname_crypto_round_up() -
|
|
||||||
*
|
|
||||||
* Return: The next multiple of block size
|
|
||||||
*/
|
|
||||||
u32 f2fs_fname_crypto_round_up(u32 size, u32 blksize)
|
|
||||||
{
|
{
|
||||||
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
|
* Allocates an output buffer that is sufficient for the crypto operation
|
||||||
* specified by the context and the direction.
|
* specified by the context and the direction.
|
||||||
*/
|
*/
|
||||||
int f2fs_fname_crypto_alloc_buffer(struct inode *inode,
|
int fscrypt_fname_alloc_buffer(struct inode *inode,
|
||||||
u32 ilen, struct f2fs_str *crypto_str)
|
u32 ilen, struct fscrypt_str *crypto_str)
|
||||||
{
|
{
|
||||||
unsigned int olen;
|
unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen);
|
||||||
int padding = 16;
|
|
||||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
|
||||||
|
|
||||||
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;
|
crypto_str->len = olen;
|
||||||
if (olen < F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
|
if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
|
||||||
olen = F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
|
olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
|
||||||
/* Allocated buffer can hold one more character to null-terminate the
|
/*
|
||||||
* string */
|
* Allocated buffer can hold one more character to null-terminate the
|
||||||
|
* string
|
||||||
|
*/
|
||||||
crypto_str->name = kmalloc(olen + 1, GFP_NOFS);
|
crypto_str->name = kmalloc(olen + 1, GFP_NOFS);
|
||||||
if (!(crypto_str->name))
|
if (!(crypto_str->name))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
return 0;
|
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.
|
* 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)
|
if (!crypto_str)
|
||||||
return;
|
return;
|
||||||
kfree(crypto_str->name);
|
kfree(crypto_str->name);
|
||||||
crypto_str->name = NULL;
|
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,
|
int fscrypt_fname_disk_to_usr(struct inode *inode,
|
||||||
f2fs_hash_t *hash,
|
u32 hash, u32 minor_hash,
|
||||||
const struct f2fs_str *iname,
|
const struct fscrypt_str *iname,
|
||||||
struct f2fs_str *oname)
|
struct fscrypt_str *oname)
|
||||||
{
|
{
|
||||||
const struct qstr qname = FSTR_TO_QSTR(iname);
|
const struct qstr qname = FSTR_TO_QSTR(iname);
|
||||||
char buf[24];
|
char buf[24];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (is_dot_dotdot(&qname)) {
|
if (fscrypt_is_dot_dotdot(&qname)) {
|
||||||
oname->name[0] = '.';
|
oname->name[0] = '.';
|
||||||
oname->name[iname->len - 1] = '.';
|
oname->name[iname->len - 1] = '.';
|
||||||
oname->len = iname->len;
|
oname->len = iname->len;
|
||||||
return oname->len;
|
return oname->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (F2FS_I(inode)->i_crypt_info)
|
if (iname->len < FS_CRYPTO_BLOCK_SIZE)
|
||||||
return f2fs_fname_decrypt(inode, iname, oname);
|
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);
|
ret = digest_encode(iname->name, iname->len, oname->name);
|
||||||
oname->len = ret;
|
oname->len = ret;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (hash) {
|
if (hash) {
|
||||||
memcpy(buf, hash, 4);
|
memcpy(buf, &hash, 4);
|
||||||
memset(buf + 4, 0, 4);
|
memcpy(buf + 4, &minor_hash, 4);
|
||||||
} else
|
} else {
|
||||||
memset(buf, 0, 8);
|
memset(buf, 0, 8);
|
||||||
|
}
|
||||||
memcpy(buf + 8, iname->name + iname->len - 16, 16);
|
memcpy(buf + 8, iname->name + iname->len - 16, 16);
|
||||||
oname->name[0] = '_';
|
oname->name[0] = '_';
|
||||||
ret = digest_encode(buf, 24, oname->name + 1);
|
ret = digest_encode(buf, 24, oname->name + 1);
|
||||||
oname->len = ret + 1;
|
oname->len = ret + 1;
|
||||||
return 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,
|
const struct qstr *iname,
|
||||||
struct f2fs_str *oname)
|
struct fscrypt_str *oname)
|
||||||
{
|
{
|
||||||
int res;
|
if (fscrypt_is_dot_dotdot(iname)) {
|
||||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
|
||||||
|
|
||||||
if (is_dot_dotdot(iname)) {
|
|
||||||
oname->name[0] = '.';
|
oname->name[0] = '.';
|
||||||
oname->name[iname->len - 1] = '.';
|
oname->name[iname->len - 1] = '.';
|
||||||
oname->len = iname->len;
|
oname->len = iname->len;
|
||||||
return oname->len;
|
return oname->len;
|
||||||
}
|
}
|
||||||
|
if (inode->i_crypt_info)
|
||||||
if (ci) {
|
return fname_encrypt(inode, iname, oname);
|
||||||
res = f2fs_fname_encrypt(inode, iname, oname);
|
/*
|
||||||
return res;
|
* Without a proper key, a user is not allowed to modify the filenames
|
||||||
}
|
|
||||||
/* 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
|
* in a directory. Consequently, a user space name cannot be mapped to
|
||||||
* a disk-space name */
|
* a disk-space name
|
||||||
|
*/
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(fscrypt_fname_usr_to_disk);
|
||||||
|
|
||||||
int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname,
|
int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||||
int lookup, struct f2fs_filename *fname)
|
int lookup, struct fscrypt_name *fname)
|
||||||
{
|
{
|
||||||
struct f2fs_crypt_info *ci;
|
|
||||||
int ret = 0, bigname = 0;
|
int ret = 0, bigname = 0;
|
||||||
|
|
||||||
memset(fname, 0, sizeof(struct f2fs_filename));
|
memset(fname, 0, sizeof(struct fscrypt_name));
|
||||||
fname->usr_fname = iname;
|
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.name = (unsigned char *)iname->name;
|
||||||
fname->disk_name.len = iname->len;
|
fname->disk_name.len = iname->len;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
ret = f2fs_get_encryption_info(dir);
|
ret = get_crypt_info(dir);
|
||||||
if (ret)
|
if (ret && ret != -EOPNOTSUPP)
|
||||||
return ret;
|
return ret;
|
||||||
ci = F2FS_I(dir)->i_crypt_info;
|
|
||||||
if (ci) {
|
if (dir->i_crypt_info) {
|
||||||
ret = f2fs_fname_crypto_alloc_buffer(dir, iname->len,
|
ret = fscrypt_fname_alloc_buffer(dir, iname->len,
|
||||||
&fname->crypto_buf);
|
&fname->crypto_buf);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
ret = f2fs_fname_encrypt(dir, iname, &fname->crypto_buf);
|
ret = fname_encrypt(dir, iname, &fname->crypto_buf);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto errout;
|
goto errout;
|
||||||
fname->disk_name.name = fname->crypto_buf.name;
|
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)
|
if (!lookup)
|
||||||
return -EACCES;
|
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
|
* user-supplied name
|
||||||
*/
|
*/
|
||||||
if (iname->name[0] == '_')
|
if (iname->name[0] == '_')
|
||||||
bigname = 1;
|
bigname = 1;
|
||||||
if ((bigname && (iname->len != 33)) ||
|
if ((bigname && (iname->len != 33)) || (!bigname && (iname->len > 43)))
|
||||||
(!bigname && (iname->len > 43)))
|
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
|
fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
|
||||||
if (fname->crypto_buf.name == NULL)
|
if (fname->crypto_buf.name == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = digest_decode(iname->name + bigname, iname->len - bigname,
|
ret = digest_decode(iname->name + bigname, iname->len - bigname,
|
||||||
fname->crypto_buf.name);
|
fname->crypto_buf.name);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -419,20 +401,24 @@ int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname,
|
|||||||
fname->crypto_buf.len = ret;
|
fname->crypto_buf.len = ret;
|
||||||
if (bigname) {
|
if (bigname) {
|
||||||
memcpy(&fname->hash, fname->crypto_buf.name, 4);
|
memcpy(&fname->hash, fname->crypto_buf.name, 4);
|
||||||
|
memcpy(&fname->minor_hash, fname->crypto_buf.name + 4, 4);
|
||||||
} else {
|
} else {
|
||||||
fname->disk_name.name = fname->crypto_buf.name;
|
fname->disk_name.name = fname->crypto_buf.name;
|
||||||
fname->disk_name.len = fname->crypto_buf.len;
|
fname->disk_name.len = fname->crypto_buf.len;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
errout:
|
errout:
|
||||||
f2fs_fname_crypto_free_buffer(&fname->crypto_buf);
|
fscrypt_fname_free_buffer(&fname->crypto_buf);
|
||||||
return ret;
|
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);
|
kfree(fname->crypto_buf.name);
|
||||||
fname->crypto_buf.name = NULL;
|
fname->crypto_buf.name = NULL;
|
||||||
fname->usr_fname = NULL;
|
fname->usr_fname = NULL;
|
||||||
fname->disk_name.name = 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
|
config F2FS_FS
|
||||||
tristate "F2FS filesystem support"
|
tristate "F2FS filesystem support"
|
||||||
depends on BLOCK
|
depends on BLOCK
|
||||||
|
select CRYPTO
|
||||||
|
select CRYPTO_CRC32
|
||||||
help
|
help
|
||||||
F2FS is based on Log-structured File System (LFS), which supports
|
F2FS is based on Log-structured File System (LFS), which supports
|
||||||
versatile "flash-friendly" features. The design has been focused on
|
versatile "flash-friendly" features. The design has been focused on
|
||||||
@ -76,15 +78,7 @@ config F2FS_FS_ENCRYPTION
|
|||||||
bool "F2FS Encryption"
|
bool "F2FS Encryption"
|
||||||
depends on F2FS_FS
|
depends on F2FS_FS
|
||||||
depends on F2FS_FS_XATTR
|
depends on F2FS_FS_XATTR
|
||||||
select CRYPTO_AES
|
select FS_ENCRYPTION
|
||||||
select CRYPTO_CBC
|
|
||||||
select CRYPTO_ECB
|
|
||||||
select CRYPTO_XTS
|
|
||||||
select CRYPTO_CTS
|
|
||||||
select CRYPTO_CTR
|
|
||||||
select CRYPTO_SHA256
|
|
||||||
select KEYS
|
|
||||||
select ENCRYPTED_KEYS
|
|
||||||
help
|
help
|
||||||
Enable encryption of f2fs files and directories. This
|
Enable encryption of f2fs files and directories. This
|
||||||
feature is similar to ecryptfs, but it is more memory
|
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_XATTR) += xattr.o
|
||||||
f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
|
f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
|
||||||
f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.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();
|
cond_resched();
|
||||||
goto repeat;
|
goto repeat;
|
||||||
}
|
}
|
||||||
f2fs_wait_on_page_writeback(page, META);
|
f2fs_wait_on_page_writeback(page, META, true);
|
||||||
SetPageUptodate(page);
|
SetPageUptodate(page);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
@ -56,7 +56,8 @@ static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index,
|
|||||||
.sbi = sbi,
|
.sbi = sbi,
|
||||||
.type = META,
|
.type = META,
|
||||||
.rw = READ_SYNC | REQ_META | REQ_PRIO,
|
.rw = READ_SYNC | REQ_META | REQ_PRIO,
|
||||||
.blk_addr = index,
|
.old_blkaddr = index,
|
||||||
|
.new_blkaddr = index,
|
||||||
.encrypted_page = NULL,
|
.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 ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
|
||||||
int type, bool sync)
|
int type, bool sync)
|
||||||
{
|
{
|
||||||
block_t prev_blk_addr = 0;
|
|
||||||
struct page *page;
|
struct page *page;
|
||||||
block_t blkno = start;
|
block_t blkno = start;
|
||||||
struct f2fs_io_info fio = {
|
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,
|
.rw = sync ? (READ_SYNC | REQ_META | REQ_PRIO) : READA,
|
||||||
.encrypted_page = NULL,
|
.encrypted_page = NULL,
|
||||||
};
|
};
|
||||||
|
struct blk_plug plug;
|
||||||
|
|
||||||
if (unlikely(type == META_POR))
|
if (unlikely(type == META_POR))
|
||||||
fio.rw &= ~REQ_META;
|
fio.rw &= ~REQ_META;
|
||||||
|
|
||||||
|
blk_start_plug(&plug);
|
||||||
for (; nrpages-- > 0; blkno++) {
|
for (; nrpages-- > 0; blkno++) {
|
||||||
|
|
||||||
if (!is_valid_blkaddr(sbi, blkno, type))
|
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)))
|
NAT_BLOCK_OFFSET(NM_I(sbi)->max_nid)))
|
||||||
blkno = 0;
|
blkno = 0;
|
||||||
/* get nat block addr */
|
/* get nat block addr */
|
||||||
fio.blk_addr = current_nat_addr(sbi,
|
fio.new_blkaddr = current_nat_addr(sbi,
|
||||||
blkno * NAT_ENTRY_PER_BLOCK);
|
blkno * NAT_ENTRY_PER_BLOCK);
|
||||||
break;
|
break;
|
||||||
case META_SIT:
|
case META_SIT:
|
||||||
/* get sit block addr */
|
/* get sit block addr */
|
||||||
fio.blk_addr = current_sit_addr(sbi,
|
fio.new_blkaddr = current_sit_addr(sbi,
|
||||||
blkno * SIT_ENTRY_PER_BLOCK);
|
blkno * SIT_ENTRY_PER_BLOCK);
|
||||||
if (blkno != start && prev_blk_addr + 1 != fio.blk_addr)
|
|
||||||
goto out;
|
|
||||||
prev_blk_addr = fio.blk_addr;
|
|
||||||
break;
|
break;
|
||||||
case META_SSA:
|
case META_SSA:
|
||||||
case META_CP:
|
case META_CP:
|
||||||
case META_POR:
|
case META_POR:
|
||||||
fio.blk_addr = blkno;
|
fio.new_blkaddr = blkno;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
|
|
||||||
page = grab_cache_page(META_MAPPING(sbi), fio.blk_addr);
|
page = grab_cache_page(META_MAPPING(sbi), fio.new_blkaddr);
|
||||||
if (!page)
|
if (!page)
|
||||||
continue;
|
continue;
|
||||||
if (PageUptodate(page)) {
|
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.page = page;
|
||||||
|
fio.old_blkaddr = fio.new_blkaddr;
|
||||||
f2fs_submit_page_mbio(&fio);
|
f2fs_submit_page_mbio(&fio);
|
||||||
f2fs_put_page(page, 0);
|
f2fs_put_page(page, 0);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
f2fs_submit_merged_bio(sbi, META, READ);
|
f2fs_submit_merged_bio(sbi, META, READ);
|
||||||
|
blk_finish_plug(&plug);
|
||||||
return blkno - start;
|
return blkno - start;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,13 +233,17 @@ static int f2fs_write_meta_page(struct page *page,
|
|||||||
if (unlikely(f2fs_cp_error(sbi)))
|
if (unlikely(f2fs_cp_error(sbi)))
|
||||||
goto redirty_out;
|
goto redirty_out;
|
||||||
|
|
||||||
f2fs_wait_on_page_writeback(page, META);
|
|
||||||
write_meta_page(sbi, page);
|
write_meta_page(sbi, page);
|
||||||
dec_page_count(sbi, F2FS_DIRTY_META);
|
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);
|
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);
|
f2fs_submit_merged_bio(sbi, META, WRITE);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
redirty_out:
|
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);
|
struct f2fs_sb_info *sbi = F2FS_M_SB(mapping);
|
||||||
long diff, written;
|
long diff, written;
|
||||||
|
|
||||||
trace_f2fs_writepages(mapping->host, wbc, META);
|
|
||||||
|
|
||||||
/* collect a number of dirty meta pages and write together */
|
/* collect a number of dirty meta pages and write together */
|
||||||
if (wbc->for_kupdate ||
|
if (wbc->for_kupdate ||
|
||||||
get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META))
|
get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META))
|
||||||
goto skip_write;
|
goto skip_write;
|
||||||
|
|
||||||
|
trace_f2fs_writepages(mapping->host, wbc, META);
|
||||||
|
|
||||||
/* if mounting is failed, skip writing node pages */
|
/* if mounting is failed, skip writing node pages */
|
||||||
mutex_lock(&sbi->cp_mutex);
|
mutex_lock(&sbi->cp_mutex);
|
||||||
diff = nr_pages_to_write(sbi, META, wbc);
|
diff = nr_pages_to_write(sbi, META, wbc);
|
||||||
@ -269,6 +274,7 @@ static int f2fs_write_meta_pages(struct address_space *mapping,
|
|||||||
|
|
||||||
skip_write:
|
skip_write:
|
||||||
wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_META);
|
wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_META);
|
||||||
|
trace_f2fs_writepages(mapping->host, wbc, META);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,15 +282,18 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type,
|
|||||||
long nr_to_write)
|
long nr_to_write)
|
||||||
{
|
{
|
||||||
struct address_space *mapping = META_MAPPING(sbi);
|
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;
|
struct pagevec pvec;
|
||||||
long nwritten = 0;
|
long nwritten = 0;
|
||||||
struct writeback_control wbc = {
|
struct writeback_control wbc = {
|
||||||
.for_reclaim = 0,
|
.for_reclaim = 0,
|
||||||
};
|
};
|
||||||
|
struct blk_plug plug;
|
||||||
|
|
||||||
pagevec_init(&pvec, 0);
|
pagevec_init(&pvec, 0);
|
||||||
|
|
||||||
|
blk_start_plug(&plug);
|
||||||
|
|
||||||
while (index <= end) {
|
while (index <= end) {
|
||||||
int i, nr_pages;
|
int i, nr_pages;
|
||||||
nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
|
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++) {
|
for (i = 0; i < nr_pages; i++) {
|
||||||
struct page *page = pvec.pages[i];
|
struct page *page = pvec.pages[i];
|
||||||
|
|
||||||
if (prev == LONG_MAX)
|
if (prev == ULONG_MAX)
|
||||||
prev = page->index - 1;
|
prev = page->index - 1;
|
||||||
if (nr_to_write != LONG_MAX && page->index != prev + 1) {
|
if (nr_to_write != LONG_MAX && page->index != prev + 1) {
|
||||||
pagevec_release(&pvec);
|
pagevec_release(&pvec);
|
||||||
@ -315,6 +324,9 @@ continue_unlock:
|
|||||||
goto continue_unlock;
|
goto continue_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f2fs_wait_on_page_writeback(page, META, true);
|
||||||
|
|
||||||
|
BUG_ON(PageWriteback(page));
|
||||||
if (!clear_page_dirty_for_io(page))
|
if (!clear_page_dirty_for_io(page))
|
||||||
goto continue_unlock;
|
goto continue_unlock;
|
||||||
|
|
||||||
@ -334,6 +346,8 @@ stop:
|
|||||||
if (nwritten)
|
if (nwritten)
|
||||||
f2fs_submit_merged_bio(sbi, type, WRITE);
|
f2fs_submit_merged_bio(sbi, type, WRITE);
|
||||||
|
|
||||||
|
blk_finish_plug(&plug);
|
||||||
|
|
||||||
return nwritten;
|
return nwritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -621,7 +635,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
|
|||||||
goto invalid_cp1;
|
goto invalid_cp1;
|
||||||
|
|
||||||
crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset)));
|
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;
|
goto invalid_cp1;
|
||||||
|
|
||||||
pre_version = cur_cp_version(cp_block);
|
pre_version = cur_cp_version(cp_block);
|
||||||
@ -636,7 +650,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
|
|||||||
goto invalid_cp2;
|
goto invalid_cp2;
|
||||||
|
|
||||||
crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset)));
|
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;
|
goto invalid_cp2;
|
||||||
|
|
||||||
cur_version = cur_cp_version(cp_block);
|
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);
|
cp_block = (struct f2fs_checkpoint *)page_address(cur_page);
|
||||||
memcpy(sbi->ckpt, cp_block, blk_size);
|
memcpy(sbi->ckpt, cp_block, blk_size);
|
||||||
|
|
||||||
|
/* Sanity checking of checkpoint */
|
||||||
|
if (sanity_check_ckpt(sbi))
|
||||||
|
goto fail_no_cp;
|
||||||
|
|
||||||
if (cp_blks <= 1)
|
if (cp_blks <= 1)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
@ -902,7 +920,7 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi)
|
|||||||
if (!get_pages(sbi, F2FS_WRITEBACK))
|
if (!get_pages(sbi, F2FS_WRITEBACK))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
io_schedule();
|
io_schedule_timeout(5*HZ);
|
||||||
}
|
}
|
||||||
finish_wait(&sbi->cp_wait, &wait);
|
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);
|
int cp_payload_blks = __cp_payload(sbi);
|
||||||
block_t discard_blk = NEXT_FREE_BLKADDR(sbi, curseg);
|
block_t discard_blk = NEXT_FREE_BLKADDR(sbi, curseg);
|
||||||
bool invalidate = false;
|
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
|
* 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_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP));
|
||||||
get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_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 *)((unsigned char *)ckpt +
|
||||||
le32_to_cpu(ckpt->checksum_offset)))
|
le32_to_cpu(ckpt->checksum_offset)))
|
||||||
= cpu_to_le32(crc32);
|
= 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);
|
write_data_summaries(sbi, start_blk);
|
||||||
start_blk += data_sum_blocks;
|
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)) {
|
if (__remain_node_summaries(cpc->reason)) {
|
||||||
write_node_summaries(sbi, start_blk);
|
write_node_summaries(sbi, start_blk);
|
||||||
start_blk += NR_CURSEG_NODE_TYPE;
|
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)))
|
if (unlikely(f2fs_cp_error(sbi)))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LONG_MAX);
|
filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LLONG_MAX);
|
||||||
filemap_fdatawait_range(META_MAPPING(sbi), 0, LONG_MAX);
|
filemap_fdatawait_range(META_MAPPING(sbi), 0, LLONG_MAX);
|
||||||
|
|
||||||
/* update user_block_counts */
|
/* update user_block_counts */
|
||||||
sbi->last_valid_block_count = sbi->total_valid_block_count;
|
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");
|
trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops");
|
||||||
|
|
||||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
f2fs_flush_merged_bios(sbi);
|
||||||
f2fs_submit_merged_bio(sbi, NODE, WRITE);
|
|
||||||
f2fs_submit_merged_bio(sbi, META, WRITE);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* update checkpoint pack index
|
* 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 (f2fs_bio_encrypted(bio)) {
|
||||||
if (bio->bi_error) {
|
if (bio->bi_error) {
|
||||||
f2fs_release_crypto_ctx(bio->bi_private);
|
fscrypt_release_ctx(bio->bi_private);
|
||||||
} else {
|
} else {
|
||||||
f2fs_end_io_crypto_work(bio->bi_private, bio);
|
fscrypt_decrypt_bio_pages(bio->bi_private, bio);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,10 +64,9 @@ static void f2fs_write_end_io(struct bio *bio)
|
|||||||
bio_for_each_segment_all(bvec, bio, i) {
|
bio_for_each_segment_all(bvec, bio, i) {
|
||||||
struct page *page = bvec->bv_page;
|
struct page *page = bvec->bv_page;
|
||||||
|
|
||||||
f2fs_restore_and_release_control_page(&page);
|
fscrypt_pullback_bio_page(&page, true);
|
||||||
|
|
||||||
if (unlikely(bio->bi_error)) {
|
if (unlikely(bio->bi_error)) {
|
||||||
set_page_dirty(page);
|
|
||||||
set_bit(AS_EIO, &page->mapping->flags);
|
set_bit(AS_EIO, &page->mapping->flags);
|
||||||
f2fs_stop_checkpoint(sbi);
|
f2fs_stop_checkpoint(sbi);
|
||||||
}
|
}
|
||||||
@ -75,8 +74,7 @@ static void f2fs_write_end_io(struct bio *bio)
|
|||||||
dec_page_count(sbi, F2FS_WRITEBACK);
|
dec_page_count(sbi, F2FS_WRITEBACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!get_pages(sbi, F2FS_WRITEBACK) &&
|
if (!get_pages(sbi, F2FS_WRITEBACK) && wq_has_sleeper(&sbi->cp_wait))
|
||||||
!list_empty(&sbi->cp_wait.task_list))
|
|
||||||
wake_up(&sbi->cp_wait);
|
wake_up(&sbi->cp_wait);
|
||||||
|
|
||||||
bio_put(bio);
|
bio_put(bio);
|
||||||
@ -116,8 +114,54 @@ static void __submit_merged_bio(struct f2fs_bio_info *io)
|
|||||||
io->bio = NULL;
|
io->bio = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
|
static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode,
|
||||||
enum page_type type, int rw)
|
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);
|
enum page_type btype = PAGE_TYPE_OF_BIO(type);
|
||||||
struct f2fs_bio_info *io;
|
struct f2fs_bio_info *io;
|
||||||
@ -126,6 +170,9 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
|
|||||||
|
|
||||||
down_write(&io->io_rwsem);
|
down_write(&io->io_rwsem);
|
||||||
|
|
||||||
|
if (!__has_merged_page(io, inode, page, ino))
|
||||||
|
goto out;
|
||||||
|
|
||||||
/* change META to META_FLUSH in the checkpoint procedure */
|
/* change META to META_FLUSH in the checkpoint procedure */
|
||||||
if (type >= META_FLUSH) {
|
if (type >= META_FLUSH) {
|
||||||
io->fio.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;
|
io->fio.rw = WRITE_FLUSH_FUA | REQ_META | REQ_PRIO;
|
||||||
}
|
}
|
||||||
__submit_merged_bio(io);
|
__submit_merged_bio(io);
|
||||||
|
out:
|
||||||
up_write(&io->io_rwsem);
|
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.
|
* Fill the locked page with data located in the block address.
|
||||||
* Return unlocked page.
|
* 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)
|
int f2fs_submit_page_bio(struct f2fs_io_info *fio)
|
||||||
{
|
{
|
||||||
struct bio *bio;
|
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);
|
trace_f2fs_submit_page_bio(page, fio);
|
||||||
f2fs_trace_ios(fio, 0);
|
f2fs_trace_ios(fio, 0);
|
||||||
|
|
||||||
/* Allocate a new bio */
|
/* 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) {
|
if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) {
|
||||||
bio_put(bio);
|
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];
|
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);
|
down_write(&io->io_rwsem);
|
||||||
|
|
||||||
if (!is_read)
|
if (!is_read)
|
||||||
inc_page_count(sbi, F2FS_WRITEBACK);
|
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))
|
io->fio.rw != fio->rw))
|
||||||
__submit_merged_bio(io);
|
__submit_merged_bio(io);
|
||||||
alloc_new:
|
alloc_new:
|
||||||
if (io->bio == NULL) {
|
if (io->bio == NULL) {
|
||||||
int bio_blocks = MAX_BIO_BLOCKS(sbi);
|
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;
|
io->fio = *fio;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +271,7 @@ alloc_new:
|
|||||||
goto 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);
|
f2fs_trace_ios(fio, 0);
|
||||||
|
|
||||||
up_write(&io->io_rwsem);
|
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;
|
struct page *node_page = dn->node_page;
|
||||||
unsigned int ofs_in_node = dn->ofs_in_node;
|
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);
|
rn = F2FS_NODE(node_page);
|
||||||
|
|
||||||
@ -229,6 +302,13 @@ void set_data_blkaddr(struct dnode_of_data *dn)
|
|||||||
dn->node_changed = true;
|
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)
|
int reserve_new_block(struct dnode_of_data *dn)
|
||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
|
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
|
||||||
@ -332,7 +412,7 @@ got_it:
|
|||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
fio.blk_addr = dn.data_blkaddr;
|
fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr;
|
||||||
fio.page = page;
|
fio.page = page;
|
||||||
err = f2fs_submit_page_bio(&fio);
|
err = f2fs_submit_page_bio(&fio);
|
||||||
if (err)
|
if (err)
|
||||||
@ -461,7 +541,6 @@ got_it:
|
|||||||
static int __allocate_data_block(struct dnode_of_data *dn)
|
static int __allocate_data_block(struct dnode_of_data *dn)
|
||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
|
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
|
||||||
struct f2fs_inode_info *fi = F2FS_I(dn->inode);
|
|
||||||
struct f2fs_summary sum;
|
struct f2fs_summary sum;
|
||||||
struct node_info ni;
|
struct node_info ni;
|
||||||
int seg = CURSEG_WARM_DATA;
|
int seg = CURSEG_WARM_DATA;
|
||||||
@ -489,7 +568,7 @@ alloc:
|
|||||||
set_data_blkaddr(dn);
|
set_data_blkaddr(dn);
|
||||||
|
|
||||||
/* update i_size */
|
/* 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;
|
dn->ofs_in_node;
|
||||||
if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT))
|
if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT))
|
||||||
i_size_write(dn->inode,
|
i_size_write(dn->inode,
|
||||||
@ -497,67 +576,33 @@ alloc:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __allocate_data_blocks(struct inode *inode, loff_t offset,
|
ssize_t f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from)
|
||||||
size_t count)
|
|
||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
struct inode *inode = file_inode(iocb->ki_filp);
|
||||||
struct dnode_of_data dn;
|
struct f2fs_map_blocks map;
|
||||||
u64 start = F2FS_BYTES_TO_BLK(offset);
|
ssize_t ret = 0;
|
||||||
u64 len = F2FS_BYTES_TO_BLK(count);
|
|
||||||
bool allocated;
|
|
||||||
u64 end_offset;
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
while (len) {
|
map.m_lblk = F2FS_BYTES_TO_BLK(iocb->ki_pos);
|
||||||
f2fs_lock_op(sbi);
|
map.m_len = F2FS_BLK_ALIGN(iov_iter_count(from));
|
||||||
|
map.m_next_pgofs = NULL;
|
||||||
|
|
||||||
/* When reading holes, we need its node page */
|
if (f2fs_encrypted_inode(inode))
|
||||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
return 0;
|
||||||
err = get_dnode_of_data(&dn, start, ALLOC_NODE);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
allocated = false;
|
if (iocb->ki_flags & IOCB_DIRECT) {
|
||||||
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
|
ret = f2fs_convert_inline_inode(inode);
|
||||||
|
if (ret)
|
||||||
while (dn.ofs_in_node < end_offset && len) {
|
return ret;
|
||||||
block_t blkaddr;
|
return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_DIO);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
return err;
|
if (iocb->ki_pos + iov_iter_count(from) > MAX_INLINE_DATA) {
|
||||||
|
ret = f2fs_convert_inline_inode(inode);
|
||||||
sync_out:
|
if (ret)
|
||||||
if (allocated)
|
return ret;
|
||||||
sync_inode_page(&dn);
|
}
|
||||||
f2fs_put_dnode(&dn);
|
if (!f2fs_has_inline_data(inode))
|
||||||
out:
|
return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO);
|
||||||
f2fs_unlock_op(sbi);
|
return ret;
|
||||||
f2fs_balance_fs(sbi, dn.node_changed);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -588,13 +633,14 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
|
|||||||
/* it only supports block size == page size */
|
/* it only supports block size == page size */
|
||||||
pgofs = (pgoff_t)map->m_lblk;
|
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_pblk = ei.blk + pgofs - ei.fofs;
|
||||||
map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs);
|
map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs);
|
||||||
map->m_flags = F2FS_MAP_MAPPED;
|
map->m_flags = F2FS_MAP_MAPPED;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
next_dnode:
|
||||||
if (create)
|
if (create)
|
||||||
f2fs_lock_op(sbi);
|
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);
|
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||||
err = get_dnode_of_data(&dn, pgofs, mode);
|
err = get_dnode_of_data(&dn, pgofs, mode);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err == -ENOENT)
|
if (err == -ENOENT) {
|
||||||
err = 0;
|
err = 0;
|
||||||
|
if (map->m_next_pgofs)
|
||||||
|
*map->m_next_pgofs =
|
||||||
|
get_next_page_offset(&dn, pgofs);
|
||||||
|
}
|
||||||
goto unlock_out;
|
goto unlock_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dn.data_blkaddr == NEW_ADDR || dn.data_blkaddr == NULL_ADDR) {
|
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
next_block:
|
||||||
blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
|
blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
|
||||||
|
|
||||||
if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) {
|
if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) {
|
||||||
@ -678,44 +668,78 @@ get_next:
|
|||||||
err = -EIO;
|
err = -EIO;
|
||||||
goto sync_out;
|
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)
|
if (err)
|
||||||
goto sync_out;
|
goto sync_out;
|
||||||
allocated = true;
|
allocated = true;
|
||||||
map->m_flags |= F2FS_MAP_NEW;
|
map->m_flags = F2FS_MAP_NEW;
|
||||||
blkaddr = dn.data_blkaddr;
|
blkaddr = dn.data_blkaddr;
|
||||||
} else {
|
} else {
|
||||||
/*
|
if (flag == F2FS_GET_BLOCK_FIEMAP &&
|
||||||
* we only merge preallocated unwritten blocks
|
blkaddr == NULL_ADDR) {
|
||||||
* for fiemap.
|
if (map->m_next_pgofs)
|
||||||
*/
|
*map->m_next_pgofs = pgofs + 1;
|
||||||
|
}
|
||||||
if (flag != F2FS_GET_BLOCK_FIEMAP ||
|
if (flag != F2FS_GET_BLOCK_FIEMAP ||
|
||||||
blkaddr != NEW_ADDR)
|
blkaddr != NEW_ADDR) {
|
||||||
|
if (flag == F2FS_GET_BLOCK_BMAP)
|
||||||
|
err = -ENOENT;
|
||||||
goto sync_out;
|
goto sync_out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Give more consecutive addresses for the readahead */
|
if (map->m_len == 0) {
|
||||||
if ((map->m_pblk != NEW_ADDR &&
|
/* 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)) ||
|
blkaddr == (map->m_pblk + ofs)) ||
|
||||||
(map->m_pblk == NEW_ADDR &&
|
(map->m_pblk == NEW_ADDR && blkaddr == NEW_ADDR) ||
|
||||||
blkaddr == NEW_ADDR)) {
|
flag == F2FS_GET_BLOCK_PRE_DIO ||
|
||||||
|
flag == F2FS_GET_BLOCK_PRE_AIO) {
|
||||||
ofs++;
|
ofs++;
|
||||||
dn.ofs_in_node++;
|
|
||||||
pgofs++;
|
|
||||||
map->m_len++;
|
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:
|
sync_out:
|
||||||
if (allocated)
|
if (allocated)
|
||||||
sync_inode_page(&dn);
|
sync_inode_page(&dn);
|
||||||
put_out:
|
|
||||||
f2fs_put_dnode(&dn);
|
f2fs_put_dnode(&dn);
|
||||||
unlock_out:
|
unlock_out:
|
||||||
if (create) {
|
if (create) {
|
||||||
f2fs_unlock_op(sbi);
|
f2fs_unlock_op(sbi);
|
||||||
f2fs_balance_fs(sbi, dn.node_changed);
|
f2fs_balance_fs(sbi, allocated);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
trace_f2fs_map_blocks(inode, map, err);
|
trace_f2fs_map_blocks(inode, map, err);
|
||||||
@ -723,13 +747,15 @@ out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
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, int create, int flag)
|
struct buffer_head *bh, int create, int flag,
|
||||||
|
pgoff_t *next_pgofs)
|
||||||
{
|
{
|
||||||
struct f2fs_map_blocks map;
|
struct f2fs_map_blocks map;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
map.m_lblk = iblock;
|
map.m_lblk = iblock;
|
||||||
map.m_len = bh->b_size >> inode->i_blkbits;
|
map.m_len = bh->b_size >> inode->i_blkbits;
|
||||||
|
map.m_next_pgofs = next_pgofs;
|
||||||
|
|
||||||
ret = f2fs_map_blocks(inode, &map, create, flag);
|
ret = f2fs_map_blocks(inode, &map, create, flag);
|
||||||
if (!ret) {
|
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,
|
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,
|
static int get_data_block_dio(struct inode *inode, sector_t iblock,
|
||||||
struct buffer_head *bh_result, int create)
|
struct buffer_head *bh_result, int create)
|
||||||
{
|
{
|
||||||
return __get_data_block(inode, iblock, bh_result, 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,
|
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 -EFBIG;
|
||||||
|
|
||||||
return __get_data_block(inode, iblock, bh_result, create,
|
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)
|
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;
|
struct buffer_head map_bh;
|
||||||
sector_t start_blk, last_blk;
|
sector_t start_blk, last_blk;
|
||||||
|
pgoff_t next_pgofs;
|
||||||
loff_t isize;
|
loff_t isize;
|
||||||
u64 logical = 0, phys = 0, size = 0;
|
u64 logical = 0, phys = 0, size = 0;
|
||||||
u32 flags = 0;
|
u32 flags = 0;
|
||||||
@ -814,14 +843,15 @@ next:
|
|||||||
map_bh.b_size = len;
|
map_bh.b_size = len;
|
||||||
|
|
||||||
ret = get_data_block(inode, start_blk, &map_bh, 0,
|
ret = get_data_block(inode, start_blk, &map_bh, 0,
|
||||||
F2FS_GET_BLOCK_FIEMAP);
|
F2FS_GET_BLOCK_FIEMAP, &next_pgofs);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* HOLE */
|
/* HOLE */
|
||||||
if (!buffer_mapped(&map_bh)) {
|
if (!buffer_mapped(&map_bh)) {
|
||||||
|
start_blk = next_pgofs;
|
||||||
/* Go through holes util pass the EOF */
|
/* 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;
|
goto prep_next;
|
||||||
/* Found a hole beyond isize means no more extents.
|
/* Found a hole beyond isize means no more extents.
|
||||||
* Note that the premise is that filesystems don't
|
* 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_lblk = 0;
|
||||||
map.m_len = 0;
|
map.m_len = 0;
|
||||||
map.m_flags = 0;
|
map.m_flags = 0;
|
||||||
|
map.m_next_pgofs = NULL;
|
||||||
|
|
||||||
for (page_idx = 0; nr_pages; page_idx++, nr_pages--) {
|
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;
|
map.m_len = last_block - block_in_file;
|
||||||
|
|
||||||
if (f2fs_map_blocks(inode, &map, 0,
|
if (f2fs_map_blocks(inode, &map, 0,
|
||||||
F2FS_GET_BLOCK_READ))
|
F2FS_GET_BLOCK_READ))
|
||||||
goto set_error_page;
|
goto set_error_page;
|
||||||
}
|
}
|
||||||
got_it:
|
got_it:
|
||||||
@ -956,12 +987,12 @@ submit_and_realloc:
|
|||||||
bio = NULL;
|
bio = NULL;
|
||||||
}
|
}
|
||||||
if (bio == NULL) {
|
if (bio == NULL) {
|
||||||
struct f2fs_crypto_ctx *ctx = NULL;
|
struct fscrypt_ctx *ctx = NULL;
|
||||||
|
|
||||||
if (f2fs_encrypted_inode(inode) &&
|
if (f2fs_encrypted_inode(inode) &&
|
||||||
S_ISREG(inode->i_mode)) {
|
S_ISREG(inode->i_mode)) {
|
||||||
|
|
||||||
ctx = f2fs_get_crypto_ctx(inode);
|
ctx = fscrypt_get_ctx(inode);
|
||||||
if (IS_ERR(ctx))
|
if (IS_ERR(ctx))
|
||||||
goto set_error_page;
|
goto set_error_page;
|
||||||
|
|
||||||
@ -974,7 +1005,7 @@ submit_and_realloc:
|
|||||||
min_t(int, nr_pages, BIO_MAX_PAGES));
|
min_t(int, nr_pages, BIO_MAX_PAGES));
|
||||||
if (!bio) {
|
if (!bio) {
|
||||||
if (ctx)
|
if (ctx)
|
||||||
f2fs_release_crypto_ctx(ctx);
|
fscrypt_release_ctx(ctx);
|
||||||
goto set_error_page;
|
goto set_error_page;
|
||||||
}
|
}
|
||||||
bio->bi_bdev = bdev;
|
bio->bi_bdev = bdev;
|
||||||
@ -1052,10 +1083,10 @@ int do_write_data_page(struct f2fs_io_info *fio)
|
|||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
fio->blk_addr = dn.data_blkaddr;
|
fio->old_blkaddr = dn.data_blkaddr;
|
||||||
|
|
||||||
/* This page is already truncated */
|
/* This page is already truncated */
|
||||||
if (fio->blk_addr == NULL_ADDR) {
|
if (fio->old_blkaddr == NULL_ADDR) {
|
||||||
ClearPageUptodate(page);
|
ClearPageUptodate(page);
|
||||||
goto out_writepage;
|
goto out_writepage;
|
||||||
}
|
}
|
||||||
@ -1064,9 +1095,9 @@ int do_write_data_page(struct f2fs_io_info *fio)
|
|||||||
|
|
||||||
/* wait for GCed encrypted page writeback */
|
/* wait for GCed encrypted page writeback */
|
||||||
f2fs_wait_on_encrypted_page_writeback(F2FS_I_SB(inode),
|
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)) {
|
if (IS_ERR(fio->encrypted_page)) {
|
||||||
err = PTR_ERR(fio->encrypted_page);
|
err = PTR_ERR(fio->encrypted_page);
|
||||||
goto out_writepage;
|
goto out_writepage;
|
||||||
@ -1079,7 +1110,7 @@ int do_write_data_page(struct f2fs_io_info *fio)
|
|||||||
* If current allocation needs SSR,
|
* If current allocation needs SSR,
|
||||||
* it had better in-place writes for updated data.
|
* 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_cold_data(page) &&
|
||||||
!IS_ATOMIC_WRITTEN_PAGE(page) &&
|
!IS_ATOMIC_WRITTEN_PAGE(page) &&
|
||||||
need_inplace_update(inode))) {
|
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);
|
trace_f2fs_do_write_data_page(page, IPU);
|
||||||
} else {
|
} else {
|
||||||
write_data_page(&dn, fio);
|
write_data_page(&dn, fio);
|
||||||
set_data_blkaddr(&dn);
|
|
||||||
f2fs_update_extent_cache(&dn);
|
|
||||||
trace_f2fs_do_write_data_page(page, OPU);
|
trace_f2fs_do_write_data_page(page, OPU);
|
||||||
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
|
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
|
||||||
if (page->index == 0)
|
if (page->index == 0)
|
||||||
@ -1177,12 +1206,18 @@ out:
|
|||||||
inode_dec_dirty_pages(inode);
|
inode_dec_dirty_pages(inode);
|
||||||
if (err)
|
if (err)
|
||||||
ClearPageUptodate(page);
|
ClearPageUptodate(page);
|
||||||
unlock_page(page);
|
|
||||||
f2fs_balance_fs(sbi, need_balance_fs);
|
if (wbc->for_reclaim) {
|
||||||
if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) {
|
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, DATA, WRITE);
|
||||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
|
||||||
remove_dirty_inode(inode);
|
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;
|
return 0;
|
||||||
|
|
||||||
redirty_out:
|
redirty_out:
|
||||||
@ -1282,7 +1317,8 @@ continue_unlock:
|
|||||||
|
|
||||||
if (PageWriteback(page)) {
|
if (PageWriteback(page)) {
|
||||||
if (wbc->sync_mode != WB_SYNC_NONE)
|
if (wbc->sync_mode != WB_SYNC_NONE)
|
||||||
f2fs_wait_on_page_writeback(page, DATA);
|
f2fs_wait_on_page_writeback(page,
|
||||||
|
DATA, true);
|
||||||
else
|
else
|
||||||
goto continue_unlock;
|
goto continue_unlock;
|
||||||
}
|
}
|
||||||
@ -1339,8 +1375,6 @@ static int f2fs_write_data_pages(struct address_space *mapping,
|
|||||||
int ret;
|
int ret;
|
||||||
long diff;
|
long diff;
|
||||||
|
|
||||||
trace_f2fs_writepages(mapping->host, wbc, DATA);
|
|
||||||
|
|
||||||
/* deal with chardevs and other special file */
|
/* deal with chardevs and other special file */
|
||||||
if (!mapping->a_ops->writepage)
|
if (!mapping->a_ops->writepage)
|
||||||
return 0;
|
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)))
|
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||||
goto skip_write;
|
goto skip_write;
|
||||||
|
|
||||||
|
trace_f2fs_writepages(mapping->host, wbc, DATA);
|
||||||
|
|
||||||
diff = nr_pages_to_write(sbi, DATA, wbc);
|
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);
|
mutex_lock(&sbi->writepages);
|
||||||
locked = true;
|
locked = true;
|
||||||
}
|
}
|
||||||
ret = f2fs_write_cache_pages(mapping, wbc, __f2fs_writepage, mapping);
|
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)
|
if (locked)
|
||||||
mutex_unlock(&sbi->writepages);
|
mutex_unlock(&sbi->writepages);
|
||||||
|
|
||||||
@ -1380,6 +1416,7 @@ static int f2fs_write_data_pages(struct address_space *mapping,
|
|||||||
|
|
||||||
skip_write:
|
skip_write:
|
||||||
wbc->pages_skipped += get_dirty_pages(inode);
|
wbc->pages_skipped += get_dirty_pages(inode);
|
||||||
|
trace_f2fs_writepages(mapping->host, wbc, DATA);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1406,6 +1443,14 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
|
|||||||
struct extent_info ei;
|
struct extent_info ei;
|
||||||
int err = 0;
|
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) ||
|
if (f2fs_has_inline_data(inode) ||
|
||||||
(pos & PAGE_CACHE_MASK) >= i_size_read(inode)) {
|
(pos & PAGE_CACHE_MASK) >= i_size_read(inode)) {
|
||||||
f2fs_lock_op(sbi);
|
f2fs_lock_op(sbi);
|
||||||
@ -1425,7 +1470,7 @@ restart:
|
|||||||
if (pos + len <= MAX_INLINE_DATA) {
|
if (pos + len <= MAX_INLINE_DATA) {
|
||||||
read_inline_data(page, ipage);
|
read_inline_data(page, ipage);
|
||||||
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
|
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
|
||||||
sync_inode_page(&dn);
|
set_inline_node(ipage);
|
||||||
} else {
|
} else {
|
||||||
err = f2fs_convert_inline_page(&dn, page);
|
err = f2fs_convert_inline_page(&dn, page);
|
||||||
if (err)
|
if (err)
|
||||||
@ -1439,13 +1484,9 @@ restart:
|
|||||||
if (f2fs_lookup_extent_cache(inode, index, &ei)) {
|
if (f2fs_lookup_extent_cache(inode, index, &ei)) {
|
||||||
dn.data_blkaddr = ei.blk + index - ei.fofs;
|
dn.data_blkaddr = ei.blk + index - ei.fofs;
|
||||||
} else {
|
} else {
|
||||||
bool restart = false;
|
|
||||||
|
|
||||||
/* hole case */
|
/* hole case */
|
||||||
err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
|
err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
|
||||||
if (err || (!err && dn.data_blkaddr == NULL_ADDR))
|
if (err || (!err && dn.data_blkaddr == NULL_ADDR)) {
|
||||||
restart = true;
|
|
||||||
if (restart) {
|
|
||||||
f2fs_put_dnode(&dn);
|
f2fs_put_dnode(&dn);
|
||||||
f2fs_lock_op(sbi);
|
f2fs_lock_op(sbi);
|
||||||
locked = true;
|
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 */
|
/* wait for GCed encrypted page writeback */
|
||||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||||
@ -1541,7 +1582,8 @@ repeat:
|
|||||||
.sbi = sbi,
|
.sbi = sbi,
|
||||||
.type = DATA,
|
.type = DATA,
|
||||||
.rw = READ_SYNC,
|
.rw = READ_SYNC,
|
||||||
.blk_addr = blkaddr,
|
.old_blkaddr = blkaddr,
|
||||||
|
.new_blkaddr = blkaddr,
|
||||||
.page = page,
|
.page = page,
|
||||||
.encrypted_page = NULL,
|
.encrypted_page = NULL,
|
||||||
};
|
};
|
||||||
@ -1561,7 +1603,7 @@ repeat:
|
|||||||
|
|
||||||
/* avoid symlink page */
|
/* avoid symlink page */
|
||||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
|
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
|
||||||
err = f2fs_decrypt_one(inode, page);
|
err = fscrypt_decrypt_page(page);
|
||||||
if (err)
|
if (err)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@ -1592,7 +1634,6 @@ static int f2fs_write_end(struct file *file,
|
|||||||
if (pos + copied > i_size_read(inode)) {
|
if (pos + copied > i_size_read(inode)) {
|
||||||
i_size_write(inode, pos + copied);
|
i_size_write(inode, pos + copied);
|
||||||
mark_inode_dirty(inode);
|
mark_inode_dirty(inode);
|
||||||
update_inode_page(inode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f2fs_put_page(page, 1);
|
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,
|
static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
|
||||||
loff_t offset)
|
loff_t offset)
|
||||||
{
|
{
|
||||||
struct file *file = iocb->ki_filp;
|
struct address_space *mapping = iocb->ki_filp->f_mapping;
|
||||||
struct address_space *mapping = file->f_mapping;
|
|
||||||
struct inode *inode = mapping->host;
|
struct inode *inode = mapping->host;
|
||||||
size_t count = iov_iter_count(iter);
|
size_t count = iov_iter_count(iter);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* we don't need to use inline_data strictly */
|
err = check_direct_IO(inode, iter, offset);
|
||||||
err = f2fs_convert_inline_inode(inode);
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||||
return 0;
|
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));
|
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);
|
err = blockdev_direct_IO(iocb, inode, iter, offset, get_data_block_dio);
|
||||||
out:
|
|
||||||
if (err < 0 && iov_iter_rw(iter) == WRITE)
|
if (err < 0 && iov_iter_rw(iter) == WRITE)
|
||||||
f2fs_write_failed(mapping, offset + count);
|
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,
|
static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
||||||
struct f2fs_filename *fname,
|
struct fscrypt_name *fname,
|
||||||
f2fs_hash_t namehash,
|
f2fs_hash_t namehash,
|
||||||
int *max_slots,
|
int *max_slots,
|
||||||
struct page **res_page)
|
struct page **res_page)
|
||||||
@ -103,15 +103,15 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
|||||||
return de;
|
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,
|
f2fs_hash_t namehash, int *max_slots,
|
||||||
struct f2fs_dentry_ptr *d)
|
struct f2fs_dentry_ptr *d)
|
||||||
{
|
{
|
||||||
struct f2fs_dir_entry *de;
|
struct f2fs_dir_entry *de;
|
||||||
unsigned long bit_pos = 0;
|
unsigned long bit_pos = 0;
|
||||||
int max_len = 0;
|
int max_len = 0;
|
||||||
struct f2fs_str de_name = FSTR_INIT(NULL, 0);
|
struct fscrypt_str de_name = FSTR_INIT(NULL, 0);
|
||||||
struct f2fs_str *name = &fname->disk_name;
|
struct fscrypt_str *name = &fname->disk_name;
|
||||||
|
|
||||||
if (max_slots)
|
if (max_slots)
|
||||||
*max_slots = 0;
|
*max_slots = 0;
|
||||||
@ -157,7 +157,7 @@ found:
|
|||||||
|
|
||||||
static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
||||||
unsigned int level,
|
unsigned int level,
|
||||||
struct f2fs_filename *fname,
|
struct fscrypt_name *fname,
|
||||||
struct page **res_page)
|
struct page **res_page)
|
||||||
{
|
{
|
||||||
struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
|
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;
|
struct f2fs_dir_entry *de = NULL;
|
||||||
unsigned int max_depth;
|
unsigned int max_depth;
|
||||||
unsigned int level;
|
unsigned int level;
|
||||||
struct f2fs_filename fname;
|
struct fscrypt_name fname;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
*res_page = NULL;
|
*res_page = NULL;
|
||||||
|
|
||||||
err = f2fs_fname_setup_filename(dir, child, 1, &fname);
|
err = fscrypt_setup_filename(dir, child, 1, &fname);
|
||||||
if (err)
|
if (err)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
f2fs_fname_free_filename(&fname);
|
fscrypt_free_filename(&fname);
|
||||||
return de;
|
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;
|
enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA;
|
||||||
lock_page(page);
|
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);
|
de->ino = cpu_to_le32(inode->i_ino);
|
||||||
set_de_type(de, inode->i_mode);
|
set_de_type(de, inode->i_mode);
|
||||||
f2fs_dentry_kunmap(dir, page);
|
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;
|
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 */
|
/* copy name info. to this inode page */
|
||||||
ri = F2FS_INODE(ipage);
|
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,
|
void do_make_empty_dir(struct inode *inode, struct inode *parent,
|
||||||
struct f2fs_dentry_ptr *d)
|
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];
|
/* update dirent of "." */
|
||||||
de->name_len = cpu_to_le16(1);
|
f2fs_update_dentry(inode->i_ino, inode->i_mode, d, &dot, 0, 0);
|
||||||
de->hash_code = 0;
|
|
||||||
de->ino = cpu_to_le32(inode->i_ino);
|
|
||||||
memcpy(d->filename[0], ".", 1);
|
|
||||||
set_de_type(de, inode->i_mode);
|
|
||||||
|
|
||||||
de = &d->dentry[1];
|
/* update dirent of ".." */
|
||||||
de->hash_code = 0;
|
f2fs_update_dentry(parent->i_ino, parent->i_mode, d, &dotdot, 0, 1);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int make_empty_dir(struct inode *inode,
|
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;
|
goto put_error;
|
||||||
|
|
||||||
if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) {
|
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)
|
if (err)
|
||||||
goto put_error;
|
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);
|
memcpy(d->filename[bit_pos], name->name, name->len);
|
||||||
de->ino = cpu_to_le32(ino);
|
de->ino = cpu_to_le32(ino);
|
||||||
set_de_type(de, mode);
|
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);
|
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_block *dentry_blk = NULL;
|
||||||
struct f2fs_dentry_ptr d;
|
struct f2fs_dentry_ptr d;
|
||||||
struct page *page = NULL;
|
struct page *page = NULL;
|
||||||
struct f2fs_filename fname;
|
struct fscrypt_name fname;
|
||||||
struct qstr new_name;
|
struct qstr new_name;
|
||||||
int slots, err;
|
int slots, err;
|
||||||
|
|
||||||
err = f2fs_fname_setup_filename(dir, name, 0, &fname);
|
err = fscrypt_setup_filename(dir, name, 0, &fname);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
@ -598,7 +592,7 @@ start:
|
|||||||
++level;
|
++level;
|
||||||
goto start;
|
goto start;
|
||||||
add_dentry:
|
add_dentry:
|
||||||
f2fs_wait_on_page_writeback(dentry_page, DATA);
|
f2fs_wait_on_page_writeback(dentry_page, DATA, true);
|
||||||
|
|
||||||
if (inode) {
|
if (inode) {
|
||||||
down_write(&F2FS_I(inode)->i_sem);
|
down_write(&F2FS_I(inode)->i_sem);
|
||||||
@ -635,7 +629,7 @@ fail:
|
|||||||
kunmap(dentry_page);
|
kunmap(dentry_page);
|
||||||
f2fs_put_page(dentry_page, 1);
|
f2fs_put_page(dentry_page, 1);
|
||||||
out:
|
out:
|
||||||
f2fs_fname_free_filename(&fname);
|
fscrypt_free_filename(&fname);
|
||||||
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
|
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
|
||||||
return err;
|
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);
|
return f2fs_delete_inline_entry(dentry, page, dir, inode);
|
||||||
|
|
||||||
lock_page(page);
|
lock_page(page);
|
||||||
f2fs_wait_on_page_writeback(page, DATA);
|
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||||
|
|
||||||
dentry_blk = page_address(page);
|
dentry_blk = page_address(page);
|
||||||
bit_pos = dentry - dentry_blk->dentry;
|
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,
|
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 char d_type = DT_UNKNOWN;
|
||||||
unsigned int bit_pos;
|
unsigned int bit_pos;
|
||||||
struct f2fs_dir_entry *de = NULL;
|
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);
|
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;
|
break;
|
||||||
|
|
||||||
de = &d->dentry[bit_pos];
|
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)
|
if (de->file_type < F2FS_FT_MAX)
|
||||||
d_type = f2fs_filetype_table[de->file_type];
|
d_type = f2fs_filetype_table[de->file_type];
|
||||||
else
|
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);
|
memcpy(de_name.name, d->filename[bit_pos], de_name.len);
|
||||||
|
|
||||||
ret = f2fs_fname_disk_to_usr(d->inode, &de->hash_code,
|
ret = fscrypt_fname_disk_to_usr(d->inode,
|
||||||
&de_name, fstr);
|
(u32)de->hash_code, 0,
|
||||||
|
&de_name, fstr);
|
||||||
kfree(de_name.name);
|
kfree(de_name.name);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return true;
|
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;
|
struct file_ra_state *ra = &file->f_ra;
|
||||||
unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK);
|
unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK);
|
||||||
struct f2fs_dentry_ptr d;
|
struct f2fs_dentry_ptr d;
|
||||||
struct f2fs_str fstr = FSTR_INIT(NULL, 0);
|
struct fscrypt_str fstr = FSTR_INIT(NULL, 0);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (f2fs_encrypted_inode(inode)) {
|
if (f2fs_encrypted_inode(inode)) {
|
||||||
err = f2fs_get_encryption_info(inode);
|
err = fscrypt_get_encryption_info(inode);
|
||||||
if (err)
|
if (err && err != -ENOKEY)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
err = f2fs_fname_crypto_alloc_buffer(inode, F2FS_NAME_LEN,
|
err = fscrypt_fname_alloc_buffer(inode, F2FS_NAME_LEN, &fstr);
|
||||||
&fstr);
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -888,15 +888,23 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
|
|||||||
f2fs_put_page(dentry_page, 1);
|
f2fs_put_page(dentry_page, 1);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
f2fs_fname_crypto_free_buffer(&fstr);
|
fscrypt_fname_free_buffer(&fstr);
|
||||||
return err;
|
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 = {
|
const struct file_operations f2fs_dir_operations = {
|
||||||
.llseek = generic_file_llseek,
|
.llseek = generic_file_llseek,
|
||||||
.read = generic_read_dir,
|
.read = generic_read_dir,
|
||||||
.iterate = f2fs_readdir,
|
.iterate = f2fs_readdir,
|
||||||
.fsync = f2fs_sync_file,
|
.fsync = f2fs_sync_file,
|
||||||
|
.open = f2fs_dir_open,
|
||||||
.unlocked_ioctl = f2fs_ioctl,
|
.unlocked_ioctl = f2fs_ioctl,
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
.compat_ioctl = f2fs_compat_ioctl,
|
.compat_ioctl = f2fs_compat_ioctl,
|
||||||
|
@ -33,6 +33,7 @@ static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi,
|
|||||||
|
|
||||||
en->ei = *ei;
|
en->ei = *ei;
|
||||||
INIT_LIST_HEAD(&en->list);
|
INIT_LIST_HEAD(&en->list);
|
||||||
|
en->et = et;
|
||||||
|
|
||||||
rb_link_node(&en->rb_node, parent, p);
|
rb_link_node(&en->rb_node, parent, p);
|
||||||
rb_insert_color(&en->rb_node, &et->root);
|
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)
|
if (et->cached_en == en)
|
||||||
et->cached_en = NULL;
|
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)
|
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,
|
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 rb_node *node, *next;
|
||||||
struct extent_node *en;
|
struct extent_node *en;
|
||||||
@ -139,18 +158,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
|
|||||||
while (node) {
|
while (node) {
|
||||||
next = rb_next(node);
|
next = rb_next(node);
|
||||||
en = rb_entry(node, struct extent_node, rb_node);
|
en = rb_entry(node, struct extent_node, rb_node);
|
||||||
|
__release_extent_node(sbi, et, en);
|
||||||
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);
|
|
||||||
}
|
|
||||||
node = next;
|
node = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,9 +240,10 @@ static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
|
|||||||
if (en) {
|
if (en) {
|
||||||
*ei = en->ei;
|
*ei = en->ei;
|
||||||
spin_lock(&sbi->extent_lock);
|
spin_lock(&sbi->extent_lock);
|
||||||
if (!list_empty(&en->list))
|
if (!list_empty(&en->list)) {
|
||||||
list_move_tail(&en->list, &sbi->extent_list);
|
list_move_tail(&en->list, &sbi->extent_list);
|
||||||
et->cached_en = en;
|
et->cached_en = en;
|
||||||
|
}
|
||||||
spin_unlock(&sbi->extent_lock);
|
spin_unlock(&sbi->extent_lock);
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
@ -329,7 +338,6 @@ lookup_neighbors:
|
|||||||
|
|
||||||
static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi,
|
static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi,
|
||||||
struct extent_tree *et, struct extent_info *ei,
|
struct extent_tree *et, struct extent_info *ei,
|
||||||
struct extent_node **den,
|
|
||||||
struct extent_node *prev_ex,
|
struct extent_node *prev_ex,
|
||||||
struct extent_node *next_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 (next_ex && __is_front_mergeable(ei, &next_ex->ei)) {
|
||||||
if (en) {
|
if (en)
|
||||||
__detach_extent_node(sbi, et, prev_ex);
|
__release_extent_node(sbi, et, prev_ex);
|
||||||
*den = prev_ex;
|
|
||||||
}
|
|
||||||
next_ex->ei.fofs = ei->fofs;
|
next_ex->ei.fofs = ei->fofs;
|
||||||
next_ex->ei.blk = ei->blk;
|
next_ex->ei.blk = ei->blk;
|
||||||
next_ex->ei.len += ei->len;
|
next_ex->ei.len += ei->len;
|
||||||
en = next_ex;
|
en = next_ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (en) {
|
if (!en)
|
||||||
__try_update_largest_extent(et, 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;
|
et->cached_en = en;
|
||||||
}
|
}
|
||||||
|
spin_unlock(&sbi->extent_lock);
|
||||||
return en;
|
return en;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +404,12 @@ do_insert:
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
__try_update_largest_extent(et, en);
|
__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;
|
et->cached_en = en;
|
||||||
|
spin_unlock(&sbi->extent_lock);
|
||||||
return en;
|
return en;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,7 +497,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
|||||||
if (parts)
|
if (parts)
|
||||||
__try_update_largest_extent(et, en);
|
__try_update_largest_extent(et, en);
|
||||||
else
|
else
|
||||||
__detach_extent_node(sbi, et, en);
|
__release_extent_node(sbi, et, en);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if original extent is split into zero or two parts, extent
|
* 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_p = NULL;
|
||||||
insert_parent = 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;
|
en = next_en;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 3. update extent in extent cache */
|
/* 3. update extent in extent cache */
|
||||||
if (blkaddr) {
|
if (blkaddr) {
|
||||||
struct extent_node *den = NULL;
|
|
||||||
|
|
||||||
set_extent_info(&ei, fofs, blkaddr, len);
|
set_extent_info(&ei, fofs, blkaddr, len);
|
||||||
en1 = __try_merge_extent_node(sbi, et, &ei, &den,
|
if (!__try_merge_extent_node(sbi, et, &ei, prev_en, next_en))
|
||||||
prev_en, next_en);
|
__insert_extent_tree(sbi, et, &ei,
|
||||||
if (!en1)
|
|
||||||
en1 = __insert_extent_tree(sbi, et, &ei,
|
|
||||||
insert_p, insert_parent);
|
insert_p, insert_parent);
|
||||||
|
|
||||||
/* give up extent_cache, if split and small updates happen */
|
/* 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;
|
et->largest.len = 0;
|
||||||
set_inode_flag(F2FS_I(inode), FI_NO_EXTENT);
|
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))
|
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);
|
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)
|
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_tree *et, *next;
|
||||||
struct extent_node *en, *tmp;
|
struct extent_node *en;
|
||||||
unsigned long ino = F2FS_ROOT_INO(sbi);
|
|
||||||
unsigned int found;
|
|
||||||
unsigned int node_cnt = 0, tree_cnt = 0;
|
unsigned int node_cnt = 0, tree_cnt = 0;
|
||||||
int remained;
|
int remained;
|
||||||
bool do_free = false;
|
|
||||||
|
|
||||||
if (!test_opt(sbi, EXTENT_CACHE))
|
if (!test_opt(sbi, EXTENT_CACHE))
|
||||||
return 0;
|
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) {
|
list_for_each_entry_safe(et, next, &sbi->zombie_list, list) {
|
||||||
if (atomic_read(&et->node_cnt)) {
|
if (atomic_read(&et->node_cnt)) {
|
||||||
write_lock(&et->lock);
|
write_lock(&et->lock);
|
||||||
node_cnt += __free_extent_tree(sbi, et, true);
|
node_cnt += __free_extent_tree(sbi, et);
|
||||||
write_unlock(&et->lock);
|
write_unlock(&et->lock);
|
||||||
}
|
}
|
||||||
|
f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
|
||||||
list_del_init(&et->list);
|
list_del_init(&et->list);
|
||||||
radix_tree_delete(&sbi->extent_tree_root, et->ino);
|
radix_tree_delete(&sbi->extent_tree_root, et->ino);
|
||||||
kmem_cache_free(extent_tree_slab, et);
|
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)
|
if (node_cnt + tree_cnt >= nr_shrink)
|
||||||
goto unlock_out;
|
goto unlock_out;
|
||||||
|
cond_resched();
|
||||||
}
|
}
|
||||||
up_write(&sbi->extent_tree_lock);
|
up_write(&sbi->extent_tree_lock);
|
||||||
|
|
||||||
@ -596,42 +581,29 @@ free_node:
|
|||||||
remained = nr_shrink - (node_cnt + tree_cnt);
|
remained = nr_shrink - (node_cnt + tree_cnt);
|
||||||
|
|
||||||
spin_lock(&sbi->extent_lock);
|
spin_lock(&sbi->extent_lock);
|
||||||
list_for_each_entry_safe(en, tmp, &sbi->extent_list, list) {
|
for (; remained > 0; remained--) {
|
||||||
if (!remained--)
|
if (list_empty(&sbi->extent_list))
|
||||||
break;
|
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);
|
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);
|
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:
|
unlock_out:
|
||||||
up_write(&sbi->extent_tree_lock);
|
up_write(&sbi->extent_tree_lock);
|
||||||
out:
|
out:
|
||||||
@ -650,7 +622,7 @@ unsigned int f2fs_destroy_extent_node(struct inode *inode)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
write_lock(&et->lock);
|
write_lock(&et->lock);
|
||||||
node_cnt = __free_extent_tree(sbi, et, true);
|
node_cnt = __free_extent_tree(sbi, et);
|
||||||
write_unlock(&et->lock);
|
write_unlock(&et->lock);
|
||||||
|
|
||||||
return node_cnt;
|
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)
|
void f2fs_update_extent_cache(struct dnode_of_data *dn)
|
||||||
{
|
{
|
||||||
struct f2fs_inode_info *fi = F2FS_I(dn->inode);
|
|
||||||
pgoff_t fofs;
|
pgoff_t fofs;
|
||||||
|
block_t blkaddr;
|
||||||
|
|
||||||
if (!f2fs_may_extent_tree(dn->inode))
|
if (!f2fs_may_extent_tree(dn->inode))
|
||||||
return;
|
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) +
|
if (f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, 1))
|
||||||
dn->ofs_in_node;
|
|
||||||
|
|
||||||
if (f2fs_update_extent_tree_range(dn->inode, fofs, dn->data_blkaddr, 1))
|
|
||||||
sync_inode_page(dn);
|
sync_inode_page(dn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
315
fs/f2fs/f2fs.h
315
fs/f2fs/f2fs.h
@ -22,10 +22,11 @@
|
|||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/bio.h>
|
#include <linux/bio.h>
|
||||||
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
||||||
|
#include <linux/fscrypto.h>
|
||||||
|
#include <crypto/hash.h>
|
||||||
|
|
||||||
#ifdef CONFIG_F2FS_CHECK_FS
|
#ifdef CONFIG_F2FS_CHECK_FS
|
||||||
#define f2fs_bug_on(sbi, condition) BUG_ON(condition)
|
#define f2fs_bug_on(sbi, condition) BUG_ON(condition)
|
||||||
#define f2fs_down_write(x, y) down_write_nest_lock(x, y)
|
|
||||||
#else
|
#else
|
||||||
#define f2fs_bug_on(sbi, condition) \
|
#define f2fs_bug_on(sbi, condition) \
|
||||||
do { \
|
do { \
|
||||||
@ -34,7 +35,6 @@
|
|||||||
set_sbi_flag(sbi, SBI_NEED_FSCK); \
|
set_sbi_flag(sbi, SBI_NEED_FSCK); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
#define f2fs_down_write(x, y) down_write(x)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -84,27 +84,6 @@ struct f2fs_mount_info {
|
|||||||
#define F2FS_CLEAR_FEATURE(sb, mask) \
|
#define F2FS_CLEAR_FEATURE(sb, mask) \
|
||||||
F2FS_SB(sb)->raw_super->feature &= ~cpu_to_le32(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
|
* For checkpoint manager
|
||||||
*/
|
*/
|
||||||
@ -183,37 +162,37 @@ struct fsync_inode_entry {
|
|||||||
block_t last_inode; /* block address locating the last inode */
|
block_t last_inode; /* block address locating the last inode */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define nats_in_cursum(sum) (le16_to_cpu(sum->n_nats))
|
#define nats_in_cursum(jnl) (le16_to_cpu(jnl->n_nats))
|
||||||
#define sits_in_cursum(sum) (le16_to_cpu(sum->n_sits))
|
#define sits_in_cursum(jnl) (le16_to_cpu(jnl->n_sits))
|
||||||
|
|
||||||
#define nat_in_journal(sum, i) (sum->nat_j.entries[i].ne)
|
#define nat_in_journal(jnl, i) (jnl->nat_j.entries[i].ne)
|
||||||
#define nid_in_journal(sum, i) (sum->nat_j.entries[i].nid)
|
#define nid_in_journal(jnl, i) (jnl->nat_j.entries[i].nid)
|
||||||
#define sit_in_journal(sum, i) (sum->sit_j.entries[i].se)
|
#define sit_in_journal(jnl, i) (jnl->sit_j.entries[i].se)
|
||||||
#define segno_in_journal(sum, i) (sum->sit_j.entries[i].segno)
|
#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_NAT_JENTRIES(jnl) (NAT_JOURNAL_ENTRIES - nats_in_cursum(jnl))
|
||||||
#define MAX_SIT_JENTRIES(sum) (SIT_JOURNAL_ENTRIES - sits_in_cursum(sum))
|
#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);
|
int before = nats_in_cursum(journal);
|
||||||
rs->n_nats = cpu_to_le16(before + i);
|
journal->n_nats = cpu_to_le16(before + i);
|
||||||
return before;
|
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);
|
int before = sits_in_cursum(journal);
|
||||||
rs->n_sits = cpu_to_le16(before + i);
|
journal->n_sits = cpu_to_le16(before + i);
|
||||||
return before;
|
return before;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
|
static inline bool __has_cursum_space(struct f2fs_journal *journal,
|
||||||
int type)
|
int size, int type)
|
||||||
{
|
{
|
||||||
if (type == NAT_JOURNAL)
|
if (type == NAT_JOURNAL)
|
||||||
return size <= MAX_NAT_JENTRIES(sum);
|
return size <= MAX_NAT_JENTRIES(journal);
|
||||||
return size <= MAX_SIT_JENTRIES(sum);
|
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_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7)
|
||||||
#define F2FS_IOC_DEFRAGMENT _IO(F2FS_IOCTL_MAGIC, 8)
|
#define F2FS_IOC_DEFRAGMENT _IO(F2FS_IOCTL_MAGIC, 8)
|
||||||
|
|
||||||
#define F2FS_IOC_SET_ENCRYPTION_POLICY \
|
#define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY
|
||||||
_IOR('f', 19, struct f2fs_encryption_policy)
|
#define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY
|
||||||
#define F2FS_IOC_GET_ENCRYPTION_PWSALT \
|
#define F2FS_IOC_GET_ENCRYPTION_PWSALT FS_IOC_GET_ENCRYPTION_PWSALT
|
||||||
_IOW('f', 20, __u8[16])
|
|
||||||
#define F2FS_IOC_GET_ENCRYPTION_POLICY \
|
|
||||||
_IOW('f', 21, struct f2fs_encryption_policy)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* should be same as XFS_IOC_GOINGDOWN.
|
* should be same as XFS_IOC_GOINGDOWN.
|
||||||
@ -268,25 +244,6 @@ struct f2fs_defragment {
|
|||||||
* For INODE and NODE manager
|
* For INODE and NODE manager
|
||||||
*/
|
*/
|
||||||
/* for directory operations */
|
/* 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 f2fs_dentry_ptr {
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
const void *bitmap;
|
const void *bitmap;
|
||||||
@ -354,6 +311,7 @@ struct extent_node {
|
|||||||
struct rb_node rb_node; /* rb node located in rb-tree */
|
struct rb_node rb_node; /* rb node located in rb-tree */
|
||||||
struct list_head list; /* node in global extent list of sbi */
|
struct list_head list; /* node in global extent list of sbi */
|
||||||
struct extent_info ei; /* extent info */
|
struct extent_info ei; /* extent info */
|
||||||
|
struct extent_tree *et; /* extent tree pointer */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct extent_tree {
|
struct extent_tree {
|
||||||
@ -382,6 +340,7 @@ struct f2fs_map_blocks {
|
|||||||
block_t m_lblk;
|
block_t m_lblk;
|
||||||
unsigned int m_len;
|
unsigned int m_len;
|
||||||
unsigned int m_flags;
|
unsigned int m_flags;
|
||||||
|
pgoff_t *m_next_pgofs; /* point next possible non-hole pgofs */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* for flag in get_data_block */
|
/* for flag in get_data_block */
|
||||||
@ -389,6 +348,8 @@ struct f2fs_map_blocks {
|
|||||||
#define F2FS_GET_BLOCK_DIO 1
|
#define F2FS_GET_BLOCK_DIO 1
|
||||||
#define F2FS_GET_BLOCK_FIEMAP 2
|
#define F2FS_GET_BLOCK_FIEMAP 2
|
||||||
#define F2FS_GET_BLOCK_BMAP 3
|
#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.
|
* 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_enc_name(inode) is_file(inode, FADVISE_ENC_NAME_BIT)
|
||||||
#define file_set_enc_name(inode) set_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
|
#define DEF_DIR_LEVEL 0
|
||||||
|
|
||||||
struct f2fs_inode_info {
|
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 dirty_list; /* linked in global dirty list */
|
||||||
struct list_head inmem_pages; /* inmemory pages managed by f2fs */
|
struct list_head inmem_pages; /* inmemory pages managed by f2fs */
|
||||||
struct mutex inmem_lock; /* lock for inmemory pages */
|
struct mutex inmem_lock; /* lock for inmemory pages */
|
||||||
|
|
||||||
struct extent_tree *extent_tree; /* cached extent_tree entry */
|
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,
|
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 */
|
nid_t next_scan_nid; /* the next nid to be scanned */
|
||||||
unsigned int ram_thresh; /* control the memory footprint */
|
unsigned int ram_thresh; /* control the memory footprint */
|
||||||
unsigned int ra_nid_pages; /* # of nid pages to be readaheaded */
|
unsigned int ra_nid_pages; /* # of nid pages to be readaheaded */
|
||||||
|
unsigned int dirty_nats_ratio; /* control dirty nats ratio threshold */
|
||||||
|
|
||||||
/* NAT cache management */
|
/* NAT cache management */
|
||||||
struct radix_tree_root nat_root;/* root of the nat entry cache */
|
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 */
|
unsigned int ofs_in_node; /* data offset in the node page */
|
||||||
bool inode_page_locked; /* inode page is locked or not */
|
bool inode_page_locked; /* inode page is locked or not */
|
||||||
bool node_changed; /* is node block changed */
|
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 */
|
block_t data_blkaddr; /* block address of the node block */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -679,6 +628,7 @@ enum page_type {
|
|||||||
META_FLUSH,
|
META_FLUSH,
|
||||||
INMEM, /* the below types are used by tracepoints only. */
|
INMEM, /* the below types are used by tracepoints only. */
|
||||||
INMEM_DROP,
|
INMEM_DROP,
|
||||||
|
INMEM_REVOKE,
|
||||||
IPU,
|
IPU,
|
||||||
OPU,
|
OPU,
|
||||||
};
|
};
|
||||||
@ -687,7 +637,8 @@ struct f2fs_io_info {
|
|||||||
struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */
|
struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */
|
||||||
enum page_type type; /* contains DATA/NODE/META/META_FLUSH */
|
enum page_type type; /* contains DATA/NODE/META/META_FLUSH */
|
||||||
int rw; /* contains R/RS/W/WS with REQ_META/REQ_PRIO */
|
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 *page; /* page to be written */
|
||||||
struct page *encrypted_page; /* encrypted page */
|
struct page *encrypted_page; /* encrypted page */
|
||||||
};
|
};
|
||||||
@ -844,8 +795,22 @@ struct f2fs_sb_info {
|
|||||||
struct list_head s_list;
|
struct list_head s_list;
|
||||||
struct mutex umount_mutex;
|
struct mutex umount_mutex;
|
||||||
unsigned int shrinker_run_no;
|
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)
|
static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type)
|
||||||
{
|
{
|
||||||
sbi->last_time[type] = jiffies;
|
sbi->last_time[type] = jiffies;
|
||||||
@ -874,6 +839,29 @@ static inline bool is_idle(struct f2fs_sb_info *sbi)
|
|||||||
/*
|
/*
|
||||||
* Inline functions
|
* 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)
|
static inline struct f2fs_inode_info *F2FS_I(struct inode *inode)
|
||||||
{
|
{
|
||||||
return container_of(inode, struct f2fs_inode_info, vfs_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)
|
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)
|
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);
|
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 - F2FS_INLINE_XATTR_ADDRS;
|
||||||
return DEF_ADDRS_PER_INODE;
|
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))
|
(F2FS_I(i)->i_acl_mode) : ((i)->i_mode))
|
||||||
|
|
||||||
/* get offset of first page in next direct node */
|
/* get offset of first page in next direct node */
|
||||||
#define PGOFS_OF_NEXT_DNODE(pgofs, fi) \
|
#define PGOFS_OF_NEXT_DNODE(pgofs, inode) \
|
||||||
((pgofs < ADDRS_PER_INODE(fi)) ? ADDRS_PER_INODE(fi) : \
|
((pgofs < ADDRS_PER_INODE(inode)) ? ADDRS_PER_INODE(inode) : \
|
||||||
(pgofs - ADDRS_PER_INODE(fi) + ADDRS_PER_BLOCK) / \
|
(pgofs - ADDRS_PER_INODE(inode) + ADDRS_PER_BLOCK) / \
|
||||||
ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi))
|
ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* file.c
|
* file.c
|
||||||
@ -1723,10 +1711,10 @@ struct dentry *f2fs_get_parent(struct dentry *child);
|
|||||||
extern unsigned char f2fs_filetype_table[F2FS_FT_MAX];
|
extern unsigned char f2fs_filetype_table[F2FS_FT_MAX];
|
||||||
void set_de_type(struct f2fs_dir_entry *, umode_t);
|
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 *);
|
f2fs_hash_t, int *, struct f2fs_dentry_ptr *);
|
||||||
bool f2fs_fill_dentries(struct dir_context *, 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 *,
|
void do_make_empty_dir(struct inode *, struct inode *,
|
||||||
struct f2fs_dentry_ptr *);
|
struct f2fs_dentry_ptr *);
|
||||||
struct page *init_inode_metadata(struct inode *, struct inode *,
|
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);
|
int f2fs_sync_fs(struct super_block *, int);
|
||||||
extern __printf(3, 4)
|
extern __printf(3, 4)
|
||||||
void f2fs_msg(struct super_block *, const char *, const char *, ...);
|
void f2fs_msg(struct super_block *, const char *, const char *, ...);
|
||||||
|
int sanity_check_ckpt(struct f2fs_sb_info *sbi);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* hash.c
|
* 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 is_checkpointed_node(struct f2fs_sb_info *, nid_t);
|
||||||
bool need_inode_block_update(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 *);
|
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 get_dnode_of_data(struct dnode_of_data *, pgoff_t, int);
|
||||||
int truncate_inode_blocks(struct inode *, pgoff_t);
|
int truncate_inode_blocks(struct inode *, pgoff_t);
|
||||||
int truncate_xattr_node(struct inode *, struct page *);
|
int truncate_xattr_node(struct inode *, struct page *);
|
||||||
@ -1811,7 +1801,8 @@ void destroy_node_manager_caches(void);
|
|||||||
* segment.c
|
* segment.c
|
||||||
*/
|
*/
|
||||||
void register_inmem_page(struct inode *, struct page *);
|
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(struct f2fs_sb_info *, bool);
|
||||||
void f2fs_balance_fs_bg(struct f2fs_sb_info *);
|
void f2fs_balance_fs_bg(struct f2fs_sb_info *);
|
||||||
int f2fs_issue_flush(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_node_page(unsigned int, struct f2fs_io_info *);
|
||||||
void write_data_page(struct dnode_of_data *, 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 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 *,
|
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 *,
|
void allocate_data_block(struct f2fs_sb_info *, struct page *,
|
||||||
block_t, block_t *, struct f2fs_summary *, int);
|
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 f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *, block_t);
|
||||||
void write_data_summaries(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);
|
void write_node_summaries(struct f2fs_sb_info *, block_t);
|
||||||
int lookup_journal_in_cursum(struct f2fs_summary_block *,
|
int lookup_journal_in_cursum(struct f2fs_journal *, int, unsigned int, int);
|
||||||
int, unsigned int, int);
|
|
||||||
void flush_sit_entries(struct f2fs_sb_info *, struct cp_control *);
|
void flush_sit_entries(struct f2fs_sb_info *, struct cp_control *);
|
||||||
int build_segment_manager(struct f2fs_sb_info *);
|
int build_segment_manager(struct f2fs_sb_info *);
|
||||||
void destroy_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
|
* data.c
|
||||||
*/
|
*/
|
||||||
void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int);
|
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 *);
|
int f2fs_submit_page_bio(struct f2fs_io_info *);
|
||||||
void f2fs_submit_page_mbio(struct f2fs_io_info *);
|
void f2fs_submit_page_mbio(struct f2fs_io_info *);
|
||||||
void set_data_blkaddr(struct dnode_of_data *);
|
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 reserve_new_block(struct dnode_of_data *);
|
||||||
int f2fs_get_block(struct dnode_of_data *, pgoff_t);
|
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);
|
int f2fs_reserve_block(struct dnode_of_data *, pgoff_t);
|
||||||
struct page *get_read_data_page(struct inode *, pgoff_t, int, bool);
|
struct page *get_read_data_page(struct inode *, pgoff_t, int, bool);
|
||||||
struct page *find_data_page(struct inode *, pgoff_t);
|
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 *);
|
int start_gc_thread(struct f2fs_sb_info *);
|
||||||
void stop_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);
|
int f2fs_gc(struct f2fs_sb_info *, bool);
|
||||||
void build_gc_manager(struct f2fs_sb_info *);
|
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 *);
|
int f2fs_write_inline_data(struct inode *, struct page *);
|
||||||
bool recover_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_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 **);
|
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 make_empty_inline_dir(struct inode *inode, struct inode *, struct page *);
|
||||||
int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *,
|
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 *);
|
struct inode *, struct inode *);
|
||||||
bool f2fs_empty_inline_dir(struct inode *);
|
bool f2fs_empty_inline_dir(struct inode *);
|
||||||
int f2fs_read_inline_dir(struct file *, struct dir_context *,
|
int f2fs_read_inline_dir(struct file *, struct dir_context *,
|
||||||
struct f2fs_str *);
|
struct fscrypt_str *);
|
||||||
int f2fs_inline_data_fiemap(struct inode *,
|
int f2fs_inline_data_fiemap(struct inode *,
|
||||||
struct fiemap_extent_info *, __u64, __u64);
|
struct fiemap_extent_info *, __u64, __u64);
|
||||||
|
|
||||||
@ -2132,13 +2129,9 @@ void destroy_extent_cache(void);
|
|||||||
/*
|
/*
|
||||||
* crypto support
|
* 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);
|
return file_is_encrypt(inode);
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void f2fs_set_encrypted_inode(struct inode *inode)
|
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)
|
static inline bool f2fs_bio_encrypted(struct bio *bio)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
return bio->bi_private != NULL;
|
||||||
return unlikely(bio->bi_private != NULL);
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int f2fs_sb_has_crypto(struct super_block *sb)
|
static inline int f2fs_sb_has_crypto(struct super_block *sb)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
|
||||||
return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT);
|
return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT);
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool f2fs_may_encrypt(struct inode *inode)
|
static inline bool f2fs_may_encrypt(struct inode *inode)
|
||||||
@ -2177,86 +2162,28 @@ static inline bool f2fs_may_encrypt(struct inode *inode)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* crypto_policy.c */
|
#ifndef CONFIG_F2FS_FS_ENCRYPTION
|
||||||
int f2fs_is_child_context_consistent_with_parent(struct inode *,
|
#define fscrypt_set_d_op(i)
|
||||||
struct inode *);
|
#define fscrypt_get_ctx fscrypt_notsupp_get_ctx
|
||||||
int f2fs_inherit_context(struct inode *, struct inode *, struct page *);
|
#define fscrypt_release_ctx fscrypt_notsupp_release_ctx
|
||||||
int f2fs_process_policy(const struct f2fs_encryption_policy *, struct inode *);
|
#define fscrypt_encrypt_page fscrypt_notsupp_encrypt_page
|
||||||
int f2fs_get_policy(struct inode *, struct f2fs_encryption_policy *);
|
#define fscrypt_decrypt_page fscrypt_notsupp_decrypt_page
|
||||||
|
#define fscrypt_decrypt_bio_pages fscrypt_notsupp_decrypt_bio_pages
|
||||||
/* crypt.c */
|
#define fscrypt_pullback_bio_page fscrypt_notsupp_pullback_bio_page
|
||||||
extern struct kmem_cache *f2fs_crypt_info_cachep;
|
#define fscrypt_restore_control_page fscrypt_notsupp_restore_control_page
|
||||||
bool f2fs_valid_contents_enc_mode(uint32_t);
|
#define fscrypt_zeroout_range fscrypt_notsupp_zeroout_range
|
||||||
uint32_t f2fs_validate_encryption_key_size(uint32_t, uint32_t);
|
#define fscrypt_process_policy fscrypt_notsupp_process_policy
|
||||||
struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *);
|
#define fscrypt_get_policy fscrypt_notsupp_get_policy
|
||||||
void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *);
|
#define fscrypt_has_permitted_context fscrypt_notsupp_has_permitted_context
|
||||||
struct page *f2fs_encrypt(struct inode *, struct page *);
|
#define fscrypt_inherit_context fscrypt_notsupp_inherit_context
|
||||||
int f2fs_decrypt(struct f2fs_crypto_ctx *, struct page *);
|
#define fscrypt_get_encryption_info fscrypt_notsupp_get_encryption_info
|
||||||
int f2fs_decrypt_one(struct inode *, struct page *);
|
#define fscrypt_put_encryption_info fscrypt_notsupp_put_encryption_info
|
||||||
void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *, struct bio *);
|
#define fscrypt_setup_filename fscrypt_notsupp_setup_filename
|
||||||
|
#define fscrypt_free_filename fscrypt_notsupp_free_filename
|
||||||
/* crypto_key.c */
|
#define fscrypt_fname_encrypted_size fscrypt_notsupp_fname_encrypted_size
|
||||||
void f2fs_free_encryption_info(struct inode *, struct f2fs_crypt_info *);
|
#define fscrypt_fname_alloc_buffer fscrypt_notsupp_fname_alloc_buffer
|
||||||
int _f2fs_get_encryption_info(struct inode *inode);
|
#define fscrypt_fname_free_buffer fscrypt_notsupp_fname_free_buffer
|
||||||
|
#define fscrypt_fname_disk_to_usr fscrypt_notsupp_fname_disk_to_usr
|
||||||
/* crypto_fname.c */
|
#define fscrypt_fname_usr_to_disk fscrypt_notsupp_fname_usr_to_disk
|
||||||
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) { }
|
|
||||||
#endif
|
#endif
|
||||||
#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);
|
trace_f2fs_vm_page_mkwrite(page, DATA);
|
||||||
mapped:
|
mapped:
|
||||||
/* fill the page */
|
/* fill the page */
|
||||||
f2fs_wait_on_page_writeback(page, DATA);
|
f2fs_wait_on_page_writeback(page, DATA, false);
|
||||||
|
|
||||||
/* wait for GCed encrypted page writeback */
|
/* wait for GCed encrypted page writeback */
|
||||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
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);
|
pagevec_init(&pvec, 0);
|
||||||
nr_pages = pagevec_lookup_tag(&pvec, mapping, &pgofs,
|
nr_pages = pagevec_lookup_tag(&pvec, mapping, &pgofs,
|
||||||
PAGECACHE_TAG_DIRTY, 1);
|
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);
|
pagevec_release(&pvec);
|
||||||
return pgofs;
|
return pgofs;
|
||||||
}
|
}
|
||||||
@ -358,15 +358,14 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
|
|||||||
} else if (err == -ENOENT) {
|
} else if (err == -ENOENT) {
|
||||||
/* direct node does not exists */
|
/* direct node does not exists */
|
||||||
if (whence == SEEK_DATA) {
|
if (whence == SEEK_DATA) {
|
||||||
pgofs = PGOFS_OF_NEXT_DNODE(pgofs,
|
pgofs = get_next_page_offset(&dn, pgofs);
|
||||||
F2FS_I(inode));
|
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
goto found;
|
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 */
|
/* find data/hole in dnode block */
|
||||||
for (; dn.ofs_in_node < end_offset;
|
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;
|
int err;
|
||||||
|
|
||||||
if (f2fs_encrypted_inode(inode)) {
|
if (f2fs_encrypted_inode(inode)) {
|
||||||
err = f2fs_get_encryption_info(inode);
|
err = fscrypt_get_encryption_info(inode);
|
||||||
if (err)
|
if (err)
|
||||||
return 0;
|
return 0;
|
||||||
|
if (!f2fs_encrypted_inode(inode))
|
||||||
|
return -ENOKEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we don't need to use inline_data strictly */
|
/* 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)
|
static int f2fs_file_open(struct inode *inode, struct file *filp)
|
||||||
{
|
{
|
||||||
int ret = generic_file_open(inode, 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)) {
|
if (!ret && f2fs_encrypted_inode(inode)) {
|
||||||
ret = f2fs_get_encryption_info(inode);
|
ret = fscrypt_get_encryption_info(inode);
|
||||||
if (ret)
|
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;
|
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.
|
* we will invalidate all blkaddr in the whole range.
|
||||||
*/
|
*/
|
||||||
fofs = start_bidx_of_node(ofs_of_node(dn->node_page),
|
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);
|
f2fs_update_extent_cache_range(dn, fofs, 0, len);
|
||||||
dec_valid_block_count(sbi, dn->inode, nr_free);
|
dec_valid_block_count(sbi, dn->inode, nr_free);
|
||||||
sync_inode_page(dn);
|
sync_inode_page(dn);
|
||||||
@ -521,9 +528,10 @@ static int truncate_partial_data_page(struct inode *inode, u64 from,
|
|||||||
if (IS_ERR(page))
|
if (IS_ERR(page))
|
||||||
return 0;
|
return 0;
|
||||||
truncate_out:
|
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);
|
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);
|
set_page_dirty(page);
|
||||||
f2fs_put_page(page, 1);
|
f2fs_put_page(page, 1);
|
||||||
return 0;
|
return 0;
|
||||||
@ -568,7 +576,7 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
|
|||||||
goto out;
|
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;
|
count -= dn.ofs_in_node;
|
||||||
f2fs_bug_on(sbi, count < 0);
|
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 (attr->ia_valid & ATTR_SIZE) {
|
||||||
if (f2fs_encrypted_inode(inode) &&
|
if (f2fs_encrypted_inode(inode) &&
|
||||||
f2fs_get_encryption_info(inode))
|
fscrypt_get_encryption_info(inode))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
if (attr->ia_size <= i_size_read(inode)) {
|
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))
|
if (IS_ERR(page))
|
||||||
return PTR_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);
|
zero_user(page, start, len);
|
||||||
set_page_dirty(page);
|
set_page_dirty(page);
|
||||||
f2fs_put_page(page, 1);
|
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;
|
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);
|
count = min(end_offset - dn.ofs_in_node, pg_end - pg_start);
|
||||||
|
|
||||||
f2fs_bug_on(F2FS_I_SB(inode), count == 0 || count > end_offset);
|
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 {
|
} else {
|
||||||
new_addr = dn.data_blkaddr;
|
new_addr = dn.data_blkaddr;
|
||||||
if (!is_checkpointed_data(sbi, new_addr)) {
|
if (!is_checkpointed_data(sbi, new_addr)) {
|
||||||
dn.data_blkaddr = NULL_ADDR;
|
|
||||||
/* do not invalidate this block address */
|
/* do not invalidate this block address */
|
||||||
set_data_blkaddr(&dn);
|
f2fs_update_data_blkaddr(&dn, NULL_ADDR);
|
||||||
f2fs_update_extent_cache(&dn);
|
|
||||||
do_replace = true;
|
do_replace = true;
|
||||||
}
|
}
|
||||||
f2fs_put_dnode(&dn);
|
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);
|
get_node_info(sbi, dn.nid, &ni);
|
||||||
f2fs_replace_block(sbi, &dn, dn.data_blkaddr, new_addr,
|
f2fs_replace_block(sbi, &dn, dn.data_blkaddr, new_addr,
|
||||||
ni.version, true);
|
ni.version, true, false);
|
||||||
f2fs_put_dnode(&dn);
|
f2fs_put_dnode(&dn);
|
||||||
} else {
|
} else {
|
||||||
struct page *psrc, *pdst;
|
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);
|
psrc = get_lock_data_page(inode, src, true);
|
||||||
if (IS_ERR(psrc))
|
if (IS_ERR(psrc))
|
||||||
return PTR_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)) {
|
if (IS_ERR(pdst)) {
|
||||||
f2fs_put_page(psrc, 1);
|
f2fs_put_page(psrc, 1);
|
||||||
return PTR_ERR(pdst);
|
return PTR_ERR(pdst);
|
||||||
@ -908,9 +914,7 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src,
|
|||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
if (!get_dnode_of_data(&dn, src, LOOKUP_NODE)) {
|
if (!get_dnode_of_data(&dn, src, LOOKUP_NODE)) {
|
||||||
dn.data_blkaddr = new_addr;
|
f2fs_update_data_blkaddr(&dn, new_addr);
|
||||||
set_data_blkaddr(&dn);
|
|
||||||
f2fs_update_extent_cache(&dn);
|
|
||||||
f2fs_put_dnode(&dn);
|
f2fs_put_dnode(&dn);
|
||||||
}
|
}
|
||||||
return ret;
|
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) {
|
if (dn.data_blkaddr != NEW_ADDR) {
|
||||||
invalidate_blocks(sbi, dn.data_blkaddr);
|
invalidate_blocks(sbi, dn.data_blkaddr);
|
||||||
|
f2fs_update_data_blkaddr(&dn, NEW_ADDR);
|
||||||
dn.data_blkaddr = NEW_ADDR;
|
|
||||||
set_data_blkaddr(&dn);
|
|
||||||
|
|
||||||
dn.data_blkaddr = NULL_ADDR;
|
|
||||||
f2fs_update_extent_cache(&dn);
|
|
||||||
}
|
}
|
||||||
f2fs_put_dnode(&dn);
|
f2fs_put_dnode(&dn);
|
||||||
f2fs_unlock_op(sbi);
|
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 */
|
/* some remained atomic pages should discarded */
|
||||||
if (f2fs_is_atomic_file(inode))
|
if (f2fs_is_atomic_file(inode))
|
||||||
commit_inmem_pages(inode, true);
|
drop_inmem_pages(inode);
|
||||||
if (f2fs_is_volatile_file(inode)) {
|
if (f2fs_is_volatile_file(inode)) {
|
||||||
set_inode_flag(F2FS_I(inode), FI_DROP_CACHE);
|
set_inode_flag(F2FS_I(inode), FI_DROP_CACHE);
|
||||||
filemap_fdatawrite(inode->i_mapping);
|
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)) {
|
if (f2fs_is_atomic_file(inode)) {
|
||||||
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
|
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
|
||||||
ret = commit_inmem_pages(inode, false);
|
ret = commit_inmem_pages(inode);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
|
set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
@ -1440,7 +1439,7 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp)
|
|||||||
|
|
||||||
if (f2fs_is_atomic_file(inode)) {
|
if (f2fs_is_atomic_file(inode)) {
|
||||||
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
|
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
|
||||||
commit_inmem_pages(inode, true);
|
drop_inmem_pages(inode);
|
||||||
}
|
}
|
||||||
if (f2fs_is_volatile_file(inode)) {
|
if (f2fs_is_volatile_file(inode)) {
|
||||||
clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
|
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)
|
static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
struct fscrypt_policy policy;
|
||||||
struct f2fs_encryption_policy policy;
|
|
||||||
struct inode *inode = file_inode(filp);
|
struct inode *inode = file_inode(filp);
|
||||||
|
|
||||||
if (copy_from_user(&policy, (struct f2fs_encryption_policy __user *)arg,
|
if (copy_from_user(&policy, (struct fscrypt_policy __user *)arg,
|
||||||
sizeof(policy)))
|
sizeof(policy)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
|
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
|
||||||
return f2fs_process_policy(&policy, inode);
|
return fscrypt_process_policy(inode, &policy);
|
||||||
#else
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg)
|
static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
struct fscrypt_policy policy;
|
||||||
struct f2fs_encryption_policy policy;
|
|
||||||
struct inode *inode = file_inode(filp);
|
struct inode *inode = file_inode(filp);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = f2fs_get_policy(inode, &policy);
|
err = fscrypt_get_policy(inode, &policy);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (copy_to_user((struct f2fs_encryption_policy __user *)arg, &policy,
|
if (copy_to_user((struct fscrypt_policy __user *)arg, &policy, sizeof(policy)))
|
||||||
sizeof(policy)))
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg)
|
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 f2fs_defragment *range)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(filp);
|
struct inode *inode = file_inode(filp);
|
||||||
struct f2fs_map_blocks map;
|
struct f2fs_map_blocks map = { .m_next_pgofs = NULL };
|
||||||
struct extent_info ei;
|
struct extent_info ei;
|
||||||
pgoff_t pg_start, pg_end;
|
pgoff_t pg_start, pg_end;
|
||||||
unsigned int blk_per_seg = sbi->blocks_per_seg;
|
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)
|
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) &&
|
if (f2fs_encrypted_inode(inode) &&
|
||||||
!f2fs_has_encryption_key(inode) &&
|
!fscrypt_has_encryption_key(inode) &&
|
||||||
f2fs_get_encryption_info(inode))
|
fscrypt_get_encryption_info(inode))
|
||||||
return -EACCES;
|
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
|
#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);
|
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.
|
* This function is called from two paths.
|
||||||
* One is garbage collection and the other is SSR segment selection.
|
* 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 dirty_seglist_info *dirty_i = DIRTY_I(sbi);
|
||||||
struct victim_sel_policy p;
|
struct victim_sel_policy p;
|
||||||
unsigned int secno, max_cost;
|
unsigned int secno, max_cost, last_victim;
|
||||||
unsigned int last_segment = MAIN_SEGS(sbi);
|
unsigned int last_segment = MAIN_SEGS(sbi);
|
||||||
int nsearched = 0;
|
unsigned int nsearched = 0;
|
||||||
|
|
||||||
mutex_lock(&dirty_i->seglist_lock);
|
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)
|
if (p.max_search == 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
last_victim = sbi->last_victim[p.gc_mode];
|
||||||
if (p.alloc_mode == LFS && gc_type == FG_GC) {
|
if (p.alloc_mode == LFS && gc_type == FG_GC) {
|
||||||
p.min_segno = check_bg_victims(sbi);
|
p.min_segno = check_bg_victims(sbi);
|
||||||
if (p.min_segno != NULL_SEGNO)
|
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;
|
p.offset = segno + p.ofs_unit;
|
||||||
if (p.ofs_unit > 1)
|
if (p.ofs_unit > 1) {
|
||||||
p.offset -= segno % p.ofs_unit;
|
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);
|
secno = GET_SECNO(sbi, segno);
|
||||||
|
|
||||||
if (sec_usage_check(sbi, secno))
|
if (sec_usage_check(sbi, secno))
|
||||||
continue;
|
goto next;
|
||||||
if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap))
|
if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap))
|
||||||
continue;
|
goto next;
|
||||||
|
|
||||||
cost = get_gc_cost(sbi, segno, &p);
|
cost = get_gc_cost(sbi, segno, &p);
|
||||||
|
|
||||||
if (p.min_cost > cost) {
|
if (p.min_cost > cost) {
|
||||||
p.min_segno = segno;
|
p.min_segno = segno;
|
||||||
p.min_cost = cost;
|
p.min_cost = cost;
|
||||||
} else if (unlikely(cost == max_cost)) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
next:
|
||||||
if (nsearched++ >= p.max_search) {
|
if (nsearched >= p.max_search) {
|
||||||
sbi->last_victim[p.gc_mode] = segno;
|
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;
|
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)
|
* On validity, copy that node with cold status, otherwise (invalid node)
|
||||||
* ignore that.
|
* 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)
|
struct f2fs_summary *sum, unsigned int segno, int gc_type)
|
||||||
{
|
{
|
||||||
bool initial = true;
|
bool initial = true;
|
||||||
@ -419,7 +440,7 @@ next_step:
|
|||||||
|
|
||||||
/* stop BG_GC if there is not enough free sections. */
|
/* stop BG_GC if there is not enough free sections. */
|
||||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
|
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
if (check_valid_map(sbi, segno, off) == 0)
|
if (check_valid_map(sbi, segno, off) == 0)
|
||||||
continue;
|
continue;
|
||||||
@ -446,7 +467,7 @@ next_step:
|
|||||||
|
|
||||||
/* set page dirty and write it */
|
/* set page dirty and write it */
|
||||||
if (gc_type == FG_GC) {
|
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);
|
set_page_dirty(node_page);
|
||||||
} else {
|
} else {
|
||||||
if (!PageWriteback(node_page))
|
if (!PageWriteback(node_page))
|
||||||
@ -460,20 +481,6 @@ next_step:
|
|||||||
initial = false;
|
initial = false;
|
||||||
goto next_step;
|
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
|
* as indirect or double indirect node blocks, are given, it must be a caller's
|
||||||
* bug.
|
* 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 indirect_blks = 2 * NIDS_PER_BLOCK + 4;
|
||||||
unsigned int bidx;
|
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);
|
int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
|
||||||
bidx = node_ofs - 5 - dec;
|
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,
|
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 f2fs_summary sum;
|
||||||
struct node_info ni;
|
struct node_info ni;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
|
block_t newaddr;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* do not read out */
|
/* 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
|
* don't cache encrypted data into meta inode until previous dirty
|
||||||
* data were writebacked to avoid racing between GC and flush.
|
* 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);
|
get_node_info(fio.sbi, dn.nid, &ni);
|
||||||
set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
|
set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
|
||||||
|
|
||||||
/* read page */
|
/* read page */
|
||||||
fio.page = 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),
|
allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr,
|
||||||
fio.blk_addr,
|
&sum, CURSEG_COLD_DATA);
|
||||||
FGP_LOCK|FGP_CREAT,
|
|
||||||
GFP_NOFS);
|
fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), newaddr,
|
||||||
if (!fio.encrypted_page)
|
FGP_LOCK | FGP_CREAT, GFP_NOFS);
|
||||||
goto put_out;
|
if (!fio.encrypted_page) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto recover_block;
|
||||||
|
}
|
||||||
|
|
||||||
err = f2fs_submit_page_bio(&fio);
|
err = f2fs_submit_page_bio(&fio);
|
||||||
if (err)
|
if (err)
|
||||||
@ -590,33 +601,39 @@ static void move_encrypted_block(struct inode *inode, block_t bidx)
|
|||||||
/* write page */
|
/* write page */
|
||||||
lock_page(fio.encrypted_page);
|
lock_page(fio.encrypted_page);
|
||||||
|
|
||||||
if (unlikely(!PageUptodate(fio.encrypted_page)))
|
if (unlikely(!PageUptodate(fio.encrypted_page))) {
|
||||||
|
err = -EIO;
|
||||||
goto put_page_out;
|
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;
|
goto put_page_out;
|
||||||
|
}
|
||||||
|
|
||||||
set_page_dirty(fio.encrypted_page);
|
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))
|
if (clear_page_dirty_for_io(fio.encrypted_page))
|
||||||
dec_page_count(fio.sbi, F2FS_DIRTY_META);
|
dec_page_count(fio.sbi, F2FS_DIRTY_META);
|
||||||
|
|
||||||
set_page_writeback(fio.encrypted_page);
|
set_page_writeback(fio.encrypted_page);
|
||||||
|
|
||||||
/* allocate block address */
|
/* allocate block address */
|
||||||
f2fs_wait_on_page_writeback(dn.node_page, NODE);
|
f2fs_wait_on_page_writeback(dn.node_page, NODE, true);
|
||||||
allocate_data_block(fio.sbi, NULL, fio.blk_addr,
|
|
||||||
&fio.blk_addr, &sum, CURSEG_COLD_DATA);
|
|
||||||
fio.rw = WRITE_SYNC;
|
fio.rw = WRITE_SYNC;
|
||||||
|
fio.new_blkaddr = newaddr;
|
||||||
f2fs_submit_page_mbio(&fio);
|
f2fs_submit_page_mbio(&fio);
|
||||||
|
|
||||||
dn.data_blkaddr = fio.blk_addr;
|
f2fs_update_data_blkaddr(&dn, newaddr);
|
||||||
set_data_blkaddr(&dn);
|
|
||||||
f2fs_update_extent_cache(&dn);
|
|
||||||
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
|
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
|
||||||
if (page->index == 0)
|
if (page->index == 0)
|
||||||
set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN);
|
set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN);
|
||||||
put_page_out:
|
put_page_out:
|
||||||
f2fs_put_page(fio.encrypted_page, 1);
|
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:
|
put_out:
|
||||||
f2fs_put_dnode(&dn);
|
f2fs_put_dnode(&dn);
|
||||||
out:
|
out:
|
||||||
@ -645,7 +662,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
|
|||||||
.encrypted_page = NULL,
|
.encrypted_page = NULL,
|
||||||
};
|
};
|
||||||
set_page_dirty(page);
|
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))
|
if (clear_page_dirty_for_io(page))
|
||||||
inode_dec_dirty_pages(inode);
|
inode_dec_dirty_pages(inode);
|
||||||
set_cold_data(page);
|
set_cold_data(page);
|
||||||
@ -663,7 +680,7 @@ out:
|
|||||||
* If the parent node is not valid or the data block address is different,
|
* If the parent node is not valid or the data block address is different,
|
||||||
* the victim data block is ignored.
|
* 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 gc_inode_list *gc_list, unsigned int segno, int gc_type)
|
||||||
{
|
{
|
||||||
struct super_block *sb = sbi->sb;
|
struct super_block *sb = sbi->sb;
|
||||||
@ -686,7 +703,7 @@ next_step:
|
|||||||
|
|
||||||
/* stop BG_GC if there is not enough free sections. */
|
/* stop BG_GC if there is not enough free sections. */
|
||||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
|
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
if (check_valid_map(sbi, segno, off) == 0)
|
if (check_valid_map(sbi, segno, off) == 0)
|
||||||
continue;
|
continue;
|
||||||
@ -719,7 +736,7 @@ next_step:
|
|||||||
continue;
|
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,
|
data_page = get_read_data_page(inode,
|
||||||
start_bidx + ofs_in_node, READA, true);
|
start_bidx + ofs_in_node, READA, true);
|
||||||
if (IS_ERR(data_page)) {
|
if (IS_ERR(data_page)) {
|
||||||
@ -735,7 +752,7 @@ next_step:
|
|||||||
/* phase 3 */
|
/* phase 3 */
|
||||||
inode = find_gc_inode(gc_list, dni.ino);
|
inode = find_gc_inode(gc_list, dni.ino);
|
||||||
if (inode) {
|
if (inode) {
|
||||||
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode))
|
start_bidx = start_bidx_of_node(nofs, inode)
|
||||||
+ ofs_in_node;
|
+ ofs_in_node;
|
||||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||||
move_encrypted_block(inode, start_bidx);
|
move_encrypted_block(inode, start_bidx);
|
||||||
@ -747,15 +764,6 @@ next_step:
|
|||||||
|
|
||||||
if (++phase < 4)
|
if (++phase < 4)
|
||||||
goto next_step;
|
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,
|
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;
|
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 gc_inode_list *gc_list, int gc_type)
|
||||||
{
|
{
|
||||||
struct page *sum_page;
|
struct page *sum_page;
|
||||||
struct f2fs_summary_block *sum;
|
struct f2fs_summary_block *sum;
|
||||||
struct blk_plug plug;
|
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 */
|
/* readahead multi ssa blocks those have contiguous address */
|
||||||
sum_page = get_sum_page(sbi, segno);
|
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);
|
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);
|
||||||
|
|
||||||
/*
|
sum = page_address(sum_page);
|
||||||
* this is to avoid deadlock:
|
f2fs_bug_on(sbi, type != GET_SUM_TYPE((&sum->footer)));
|
||||||
* - 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);
|
|
||||||
|
|
||||||
switch (GET_SUM_TYPE((&sum->footer))) {
|
/*
|
||||||
case SUM_TYPE_NODE:
|
* this is to avoid deadlock:
|
||||||
nfree = gc_node_segment(sbi, sum->entries, segno, gc_type);
|
* - lock_page(sum_page) - f2fs_replace_block
|
||||||
break;
|
* - check_valid_map() - mutex_lock(sentry_lock)
|
||||||
case SUM_TYPE_DATA:
|
* - mutex_lock(sentry_lock) - change_curseg()
|
||||||
nfree = gc_data_segment(sbi, sum->entries, gc_list,
|
* - lock_page(sum_page)
|
||||||
segno, gc_type);
|
*/
|
||||||
break;
|
|
||||||
|
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);
|
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);
|
stat_inc_call_count(sbi->stat_info);
|
||||||
|
|
||||||
f2fs_put_page(sum_page, 0);
|
return seg_freed;
|
||||||
return nfree;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int f2fs_gc(struct f2fs_sb_info *sbi, bool sync)
|
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 gc_type = sync ? FG_GC : BG_GC;
|
||||||
int sec_freed = 0;
|
int sec_freed = 0, seg_freed;
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
struct cp_control cpc;
|
struct cp_control cpc;
|
||||||
struct gc_inode_list gc_list = {
|
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)) {
|
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed)) {
|
||||||
gc_type = FG_GC;
|
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))
|
if (__get_victim(sbi, &segno, gc_type) || prefree_segments(sbi))
|
||||||
write_checkpoint(sbi, &cpc);
|
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))
|
if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type))
|
||||||
goto stop;
|
goto stop;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
/* readahead multi ssa blocks those have contiguous address */
|
seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type);
|
||||||
if (sbi->segs_per_sec > 1)
|
|
||||||
ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), sbi->segs_per_sec,
|
|
||||||
META_SSA, true);
|
|
||||||
|
|
||||||
for (i = 0; i < sbi->segs_per_sec; i++) {
|
if (gc_type == FG_GC && seg_freed == sbi->segs_per_sec)
|
||||||
/*
|
|
||||||
* 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)
|
|
||||||
sec_freed++;
|
sec_freed++;
|
||||||
|
|
||||||
if (gc_type == FG_GC)
|
if (gc_type == FG_GC)
|
||||||
|
@ -71,7 +71,7 @@ bool truncate_inline_inode(struct page *ipage, u64 from)
|
|||||||
|
|
||||||
addr = inline_data_addr(ipage);
|
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);
|
memset(addr + from, 0, MAX_INLINE_DATA - from);
|
||||||
|
|
||||||
return true;
|
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)
|
int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
||||||
{
|
{
|
||||||
void *src_addr, *dst_addr;
|
|
||||||
struct f2fs_io_info fio = {
|
struct f2fs_io_info fio = {
|
||||||
.sbi = F2FS_I_SB(dn->inode),
|
.sbi = F2FS_I_SB(dn->inode),
|
||||||
.type = DATA,
|
.type = DATA,
|
||||||
@ -115,8 +114,6 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
|||||||
};
|
};
|
||||||
int dirty, err;
|
int dirty, err;
|
||||||
|
|
||||||
f2fs_bug_on(F2FS_I_SB(dn->inode), page->index);
|
|
||||||
|
|
||||||
if (!f2fs_exist_data(dn->inode))
|
if (!f2fs_exist_data(dn->inode))
|
||||||
goto clear_out;
|
goto clear_out;
|
||||||
|
|
||||||
@ -124,21 +121,9 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
|||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
f2fs_wait_on_page_writeback(page, DATA);
|
f2fs_bug_on(F2FS_P_SB(page), PageWriteback(page));
|
||||||
|
|
||||||
if (PageUptodate(page))
|
read_inline_data(page, dn->inode_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:
|
|
||||||
set_page_dirty(page);
|
set_page_dirty(page);
|
||||||
|
|
||||||
/* clear dirty state */
|
/* clear dirty state */
|
||||||
@ -146,11 +131,9 @@ no_update:
|
|||||||
|
|
||||||
/* write data page to try to make data consistent */
|
/* write data page to try to make data consistent */
|
||||||
set_page_writeback(page);
|
set_page_writeback(page);
|
||||||
fio.blk_addr = dn->data_blkaddr;
|
fio.old_blkaddr = dn->data_blkaddr;
|
||||||
write_data_page(dn, &fio);
|
write_data_page(dn, &fio);
|
||||||
set_data_blkaddr(dn);
|
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||||
f2fs_update_extent_cache(dn);
|
|
||||||
f2fs_wait_on_page_writeback(page, DATA);
|
|
||||||
if (dirty)
|
if (dirty)
|
||||||
inode_dec_dirty_pages(dn->inode);
|
inode_dec_dirty_pages(dn->inode);
|
||||||
|
|
||||||
@ -159,6 +142,7 @@ no_update:
|
|||||||
|
|
||||||
/* clear inline data and flag after data writeback */
|
/* clear inline data and flag after data writeback */
|
||||||
truncate_inline_inode(dn->inode_page, 0);
|
truncate_inline_inode(dn->inode_page, 0);
|
||||||
|
clear_inline_node(dn->inode_page);
|
||||||
clear_out:
|
clear_out:
|
||||||
stat_dec_inline_inode(dn->inode);
|
stat_dec_inline_inode(dn->inode);
|
||||||
f2fs_clear_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_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);
|
src_addr = kmap_atomic(page);
|
||||||
dst_addr = inline_data_addr(dn.inode_page);
|
dst_addr = inline_data_addr(dn.inode_page);
|
||||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
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);
|
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
|
||||||
|
|
||||||
sync_inode_page(&dn);
|
sync_inode_page(&dn);
|
||||||
|
clear_inline_node(dn.inode_page);
|
||||||
f2fs_put_dnode(&dn);
|
f2fs_put_dnode(&dn);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -261,7 +246,7 @@ process_inline:
|
|||||||
ipage = get_node_page(sbi, inode->i_ino);
|
ipage = get_node_page(sbi, inode->i_ino);
|
||||||
f2fs_bug_on(sbi, IS_ERR(ipage));
|
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);
|
src_addr = inline_data_addr(npage);
|
||||||
dst_addr = inline_data_addr(ipage);
|
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_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_sb_info *sbi = F2FS_SB(dir->i_sb);
|
||||||
struct f2fs_inline_dentry *inline_dentry;
|
struct f2fs_inline_dentry *inline_dentry;
|
||||||
@ -389,7 +374,7 @@ static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage,
|
|||||||
if (err)
|
if (err)
|
||||||
goto out;
|
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);
|
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
|
||||||
|
|
||||||
dentry_blk = kmap_atomic(page);
|
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);
|
name_hash = f2fs_dentry_hash(name);
|
||||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
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;
|
int i;
|
||||||
|
|
||||||
lock_page(page);
|
lock_page(page);
|
||||||
f2fs_wait_on_page_writeback(page, NODE);
|
f2fs_wait_on_page_writeback(page, NODE, true);
|
||||||
|
|
||||||
inline_dentry = inline_data_addr(page);
|
inline_dentry = inline_data_addr(page);
|
||||||
bit_pos = dentry - inline_dentry->dentry;
|
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,
|
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 inode *inode = file_inode(file);
|
||||||
struct f2fs_inline_dentry *inline_dentry = NULL;
|
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) {
|
while (start < end) {
|
||||||
if (*start++) {
|
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_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
|
||||||
set_raw_inline(F2FS_I(inode), F2FS_INODE(ipage));
|
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;
|
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);
|
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);
|
set_cold_node(inode, node_page);
|
||||||
clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
|
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);
|
return set_page_dirty(node_page);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +324,7 @@ void f2fs_evict_inode(struct inode *inode)
|
|||||||
|
|
||||||
/* some remained atomic pages should discarded */
|
/* some remained atomic pages should discarded */
|
||||||
if (f2fs_is_atomic_file(inode))
|
if (f2fs_is_atomic_file(inode))
|
||||||
commit_inmem_pages(inode, true);
|
drop_inmem_pages(inode);
|
||||||
|
|
||||||
trace_f2fs_evict_inode(inode);
|
trace_f2fs_evict_inode(inode);
|
||||||
truncate_inode_pages_final(&inode->i_data);
|
truncate_inode_pages_final(&inode->i_data);
|
||||||
@ -385,10 +389,7 @@ no_delete:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
out_clear:
|
out_clear:
|
||||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
fscrypt_put_encryption_info(inode, NULL);
|
||||||
if (fi->i_crypt_info)
|
|
||||||
f2fs_free_encryption_info(inode, fi->i_crypt_info);
|
|
||||||
#endif
|
|
||||||
clear_inode(inode);
|
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;
|
int err;
|
||||||
|
|
||||||
if (f2fs_encrypted_inode(dir) &&
|
if (f2fs_encrypted_inode(dir) &&
|
||||||
!f2fs_is_child_context_consistent_with_parent(dir, inode))
|
!fscrypt_has_permitted_context(dir, inode))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
f2fs_balance_fs(sbi, true);
|
f2fs_balance_fs(sbi, true);
|
||||||
@ -260,6 +260,22 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
struct page *page;
|
struct page *page;
|
||||||
nid_t ino;
|
nid_t ino;
|
||||||
int err = 0;
|
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)
|
if (dentry->d_name.len > F2FS_NAME_LEN)
|
||||||
return ERR_PTR(-ENAMETOOLONG);
|
return ERR_PTR(-ENAMETOOLONG);
|
||||||
@ -276,15 +292,29 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
if (IS_ERR(inode))
|
if (IS_ERR(inode))
|
||||||
return ERR_CAST(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)) {
|
if (f2fs_has_inline_dots(inode)) {
|
||||||
err = __recover_dot_dentries(inode, dir->i_ino);
|
err = __recover_dot_dentries(inode, dir->i_ino);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_out;
|
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);
|
return d_splice_alias(inode, dentry);
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
iget_failed(inode);
|
iput(inode);
|
||||||
return ERR_PTR(err);
|
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 f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
size_t len = strlen(symname);
|
size_t len = strlen(symname);
|
||||||
size_t p_len;
|
struct fscrypt_str disk_link = FSTR_INIT((char *)symname, len + 1);
|
||||||
char *p_str;
|
struct fscrypt_symlink_data *sd = NULL;
|
||||||
struct f2fs_str disk_link = FSTR_INIT(NULL, 0);
|
|
||||||
struct f2fs_encrypted_symlink_data *sd = NULL;
|
|
||||||
int err;
|
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;
|
return -ENAMETOOLONG;
|
||||||
|
|
||||||
inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
|
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);
|
f2fs_unlock_op(sbi);
|
||||||
alloc_nid_done(sbi, inode->i_ino);
|
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 qstr istr = QSTR_INIT(symname, len);
|
||||||
|
struct fscrypt_str ostr;
|
||||||
|
|
||||||
err = f2fs_get_encryption_info(inode);
|
sd = kzalloc(disk_link.len, GFP_NOFS);
|
||||||
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);
|
|
||||||
if (!sd) {
|
if (!sd) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
memcpy(sd->encrypted_path, disk_link.name, disk_link.len);
|
|
||||||
sd->len = cpu_to_le16(disk_link.len);
|
err = fscrypt_get_encryption_info(inode);
|
||||||
p_str = (char *)sd;
|
if (err)
|
||||||
} else {
|
goto err_out;
|
||||||
p_len = len + 1;
|
|
||||||
p_str = (char *)symname;
|
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:
|
err_out:
|
||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
@ -425,7 +459,8 @@ err_out:
|
|||||||
* performance regression.
|
* performance regression.
|
||||||
*/
|
*/
|
||||||
if (!err) {
|
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))
|
if (IS_DIRSYNC(dir))
|
||||||
f2fs_sync_fs(sbi->sb, 1);
|
f2fs_sync_fs(sbi->sb, 1);
|
||||||
@ -434,7 +469,6 @@ err_out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
kfree(sd);
|
kfree(sd);
|
||||||
f2fs_fname_crypto_free_buffer(&disk_link);
|
|
||||||
return err;
|
return err;
|
||||||
out:
|
out:
|
||||||
handle_failed_inode(inode);
|
handle_failed_inode(inode);
|
||||||
@ -582,7 +616,7 @@ out:
|
|||||||
static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
{
|
{
|
||||||
if (f2fs_encrypted_inode(dir)) {
|
if (f2fs_encrypted_inode(dir)) {
|
||||||
int err = f2fs_get_encryption_info(dir);
|
int err = fscrypt_get_encryption_info(dir);
|
||||||
if (err)
|
if (err)
|
||||||
return 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_dir_entry = NULL;
|
||||||
struct f2fs_dir_entry *old_entry;
|
struct f2fs_dir_entry *old_entry;
|
||||||
struct f2fs_dir_entry *new_entry;
|
struct f2fs_dir_entry *new_entry;
|
||||||
|
bool is_old_inline = f2fs_has_inline_dentry(old_dir);
|
||||||
int err = -ENOENT;
|
int err = -ENOENT;
|
||||||
|
|
||||||
if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) &&
|
if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) &&
|
||||||
!f2fs_is_child_context_consistent_with_parent(new_dir,
|
!fscrypt_has_permitted_context(new_dir, old_inode)) {
|
||||||
old_inode)) {
|
|
||||||
err = -EPERM;
|
err = -EPERM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -654,8 +688,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||||||
if (err)
|
if (err)
|
||||||
goto put_out_dir;
|
goto put_out_dir;
|
||||||
|
|
||||||
if (update_dent_inode(old_inode, new_inode,
|
err = update_dent_inode(old_inode, new_inode,
|
||||||
&new_dentry->d_name)) {
|
&new_dentry->d_name);
|
||||||
|
if (err) {
|
||||||
release_orphan_inode(sbi);
|
release_orphan_inode(sbi);
|
||||||
goto put_out_dir;
|
goto put_out_dir;
|
||||||
}
|
}
|
||||||
@ -693,6 +728,26 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||||||
inc_nlink(new_dir);
|
inc_nlink(new_dir);
|
||||||
update_inode_page(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);
|
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;
|
int err = -ENOENT;
|
||||||
|
|
||||||
if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) &&
|
if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) &&
|
||||||
(old_dir != new_dir) &&
|
(old_dir != new_dir) &&
|
||||||
(!f2fs_is_child_context_consistent_with_parent(new_dir,
|
(!fscrypt_has_permitted_context(new_dir, old_inode) ||
|
||||||
old_inode) ||
|
!fscrypt_has_permitted_context(old_dir, new_inode)))
|
||||||
!f2fs_is_child_context_consistent_with_parent(old_dir,
|
|
||||||
new_inode)))
|
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
|
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);
|
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,
|
static const char *f2fs_encrypted_get_link(struct dentry *dentry,
|
||||||
struct inode *inode,
|
struct inode *inode,
|
||||||
struct delayed_call *done)
|
struct delayed_call *done)
|
||||||
{
|
{
|
||||||
struct page *cpage = NULL;
|
struct page *cpage = NULL;
|
||||||
char *caddr, *paddr = NULL;
|
char *caddr, *paddr = NULL;
|
||||||
struct f2fs_str cstr = FSTR_INIT(NULL, 0);
|
struct fscrypt_str cstr = FSTR_INIT(NULL, 0);
|
||||||
struct f2fs_str pstr = FSTR_INIT(NULL, 0);
|
struct fscrypt_str pstr = FSTR_INIT(NULL, 0);
|
||||||
struct f2fs_encrypted_symlink_data *sd;
|
struct fscrypt_symlink_data *sd;
|
||||||
loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
|
loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
|
||||||
u32 max_size = inode->i_sb->s_blocksize;
|
u32 max_size = inode->i_sb->s_blocksize;
|
||||||
int res;
|
int res;
|
||||||
@ -954,7 +1006,7 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
|
|||||||
if (!dentry)
|
if (!dentry)
|
||||||
return ERR_PTR(-ECHILD);
|
return ERR_PTR(-ECHILD);
|
||||||
|
|
||||||
res = f2fs_get_encryption_info(inode);
|
res = fscrypt_get_encryption_info(inode);
|
||||||
if (res)
|
if (res)
|
||||||
return ERR_PTR(res);
|
return ERR_PTR(res);
|
||||||
|
|
||||||
@ -965,7 +1017,8 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
|
|||||||
caddr[size] = 0;
|
caddr[size] = 0;
|
||||||
|
|
||||||
/* Symlink is encrypted */
|
/* 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);
|
cstr.len = le16_to_cpu(sd->len);
|
||||||
|
|
||||||
/* this is broken symlink case */
|
/* this is broken symlink case */
|
||||||
@ -973,12 +1026,6 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
|
|||||||
res = -ENOENT;
|
res = -ENOENT;
|
||||||
goto errout;
|
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 */
|
/* this is broken symlink case */
|
||||||
if (unlikely(cstr.name[0] == 0)) {
|
if (unlikely(cstr.name[0] == 0)) {
|
||||||
@ -986,22 +1033,19 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry,
|
|||||||
goto errout;
|
goto errout;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cstr.len + sizeof(struct f2fs_encrypted_symlink_data) - 1) >
|
if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) {
|
||||||
max_size) {
|
|
||||||
/* Symlink data on the disk is corrupted */
|
/* Symlink data on the disk is corrupted */
|
||||||
res = -EIO;
|
res = -EIO;
|
||||||
goto errout;
|
goto errout;
|
||||||
}
|
}
|
||||||
res = f2fs_fname_crypto_alloc_buffer(inode, cstr.len, &pstr);
|
res = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
|
||||||
if (res)
|
if (res)
|
||||||
goto errout;
|
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)
|
if (res < 0)
|
||||||
goto errout;
|
goto errout;
|
||||||
|
|
||||||
kfree(cstr.name);
|
|
||||||
|
|
||||||
paddr = pstr.name;
|
paddr = pstr.name;
|
||||||
|
|
||||||
/* Null-terminate the 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);
|
set_delayed_call(done, kfree_link, paddr);
|
||||||
return paddr;
|
return paddr;
|
||||||
errout:
|
errout:
|
||||||
kfree(cstr.name);
|
fscrypt_fname_free_buffer(&pstr);
|
||||||
f2fs_fname_crypto_free_buffer(&pstr);
|
|
||||||
page_cache_release(cpage);
|
page_cache_release(cpage);
|
||||||
return ERR_PTR(res);
|
return ERR_PTR(res);
|
||||||
}
|
}
|
||||||
@ -1029,7 +1072,6 @@ const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
|
|||||||
.removexattr = generic_removexattr,
|
.removexattr = generic_removexattr,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
const struct inode_operations f2fs_dir_inode_operations = {
|
const struct inode_operations f2fs_dir_inode_operations = {
|
||||||
.create = f2fs_create,
|
.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;
|
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_nat_entry *ne)
|
||||||
{
|
{
|
||||||
|
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||||
struct nat_entry *e;
|
struct nat_entry *e;
|
||||||
|
|
||||||
e = __lookup_nat_cache(nm_i, nid);
|
e = __lookup_nat_cache(nm_i, nid);
|
||||||
if (!e) {
|
if (!e) {
|
||||||
e = grab_nat_entry(nm_i, nid);
|
e = grab_nat_entry(nm_i, nid);
|
||||||
node_info_from_raw_nat(&e->ni, ne);
|
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 f2fs_nm_info *nm_i = NM_I(sbi);
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
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);
|
nid_t start_nid = START_NID(nid);
|
||||||
struct f2fs_nat_block *nat_blk;
|
struct f2fs_nat_block *nat_blk;
|
||||||
struct page *page = NULL;
|
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->ino = nat_get_ino(e);
|
||||||
ni->blk_addr = nat_get_blkaddr(e);
|
ni->blk_addr = nat_get_blkaddr(e);
|
||||||
ni->version = nat_get_version(e);
|
ni->version = nat_get_version(e);
|
||||||
}
|
up_read(&nm_i->nat_tree_lock);
|
||||||
up_read(&nm_i->nat_tree_lock);
|
|
||||||
if (e)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
memset(&ne, 0, sizeof(struct f2fs_nat_entry));
|
memset(&ne, 0, sizeof(struct f2fs_nat_entry));
|
||||||
|
|
||||||
down_write(&nm_i->nat_tree_lock);
|
|
||||||
|
|
||||||
/* Check current segment summary */
|
/* Check current segment summary */
|
||||||
mutex_lock(&curseg->curseg_mutex);
|
down_read(&curseg->journal_rwsem);
|
||||||
i = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 0);
|
i = lookup_journal_in_cursum(journal, NAT_JOURNAL, nid, 0);
|
||||||
if (i >= 0) {
|
if (i >= 0) {
|
||||||
ne = nat_in_journal(sum, i);
|
ne = nat_in_journal(journal, i);
|
||||||
node_info_from_raw_nat(ni, &ne);
|
node_info_from_raw_nat(ni, &ne);
|
||||||
}
|
}
|
||||||
mutex_unlock(&curseg->curseg_mutex);
|
up_read(&curseg->journal_rwsem);
|
||||||
if (i >= 0)
|
if (i >= 0)
|
||||||
goto cache;
|
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);
|
node_info_from_raw_nat(ni, &ne);
|
||||||
f2fs_put_page(page, 1);
|
f2fs_put_page(page, 1);
|
||||||
cache:
|
cache:
|
||||||
|
up_read(&nm_i->nat_tree_lock);
|
||||||
/* cache nat entry */
|
/* 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);
|
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.
|
* The maximum depth is four.
|
||||||
* Offset[0] will have raw inode offset.
|
* 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])
|
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 direct_blks = ADDRS_PER_BLOCK;
|
||||||
const long dptrs_per_blk = NIDS_PER_BLOCK;
|
const long dptrs_per_blk = NIDS_PER_BLOCK;
|
||||||
const long indirect_blks = ADDRS_PER_BLOCK * 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];
|
int offset[4];
|
||||||
unsigned int noffset[4];
|
unsigned int noffset[4];
|
||||||
nid_t nids[4];
|
nid_t nids[4];
|
||||||
int level, i;
|
int level, i = 0;
|
||||||
int err = 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;
|
nids[0] = dn->inode->i_ino;
|
||||||
npage[0] = dn->inode_page;
|
npage[0] = dn->inode_page;
|
||||||
@ -585,6 +620,10 @@ release_pages:
|
|||||||
release_out:
|
release_out:
|
||||||
dn->inode_page = NULL;
|
dn->inode_page = NULL;
|
||||||
dn->node_page = NULL;
|
dn->node_page = NULL;
|
||||||
|
if (err == -ENOENT) {
|
||||||
|
dn->cur_level = i;
|
||||||
|
dn->max_level = level;
|
||||||
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -792,7 +831,7 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from)
|
|||||||
|
|
||||||
trace_f2fs_truncate_inode_blocks_enter(inode, 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:
|
restart:
|
||||||
page = get_node_page(sbi, inode->i_ino);
|
page = get_node_page(sbi, inode->i_ino);
|
||||||
if (IS_ERR(page)) {
|
if (IS_ERR(page)) {
|
||||||
@ -861,7 +900,7 @@ skip_partial:
|
|||||||
f2fs_put_page(page, 1);
|
f2fs_put_page(page, 1);
|
||||||
goto restart;
|
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;
|
ri->i_nid[offset[0] - NODE_DIR1_BLOCK] = 0;
|
||||||
set_page_dirty(page);
|
set_page_dirty(page);
|
||||||
unlock_page(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;
|
new_ni.ino = dn->inode->i_ino;
|
||||||
set_node_addr(sbi, &new_ni, NEW_ADDR, false);
|
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);
|
fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true);
|
||||||
set_cold_node(dn->inode, page);
|
set_cold_node(dn->inode, page);
|
||||||
SetPageUptodate(page);
|
SetPageUptodate(page);
|
||||||
@ -1029,7 +1068,7 @@ static int read_node_page(struct page *page, int rw)
|
|||||||
if (PageUptodate(page))
|
if (PageUptodate(page))
|
||||||
return LOCKED_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);
|
return f2fs_submit_page_bio(&fio);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1045,12 +1084,11 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
|
|||||||
return;
|
return;
|
||||||
f2fs_bug_on(sbi, check_nid_range(sbi, nid));
|
f2fs_bug_on(sbi, check_nid_range(sbi, nid));
|
||||||
|
|
||||||
apage = find_get_page(NODE_MAPPING(sbi), nid);
|
rcu_read_lock();
|
||||||
if (apage && PageUptodate(apage)) {
|
apage = radix_tree_lookup(&NODE_MAPPING(sbi)->page_tree, nid);
|
||||||
f2fs_put_page(apage, 0);
|
rcu_read_unlock();
|
||||||
|
if (apage)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
f2fs_put_page(apage, 0);
|
|
||||||
|
|
||||||
apage = grab_cache_page(NODE_MAPPING(sbi), nid);
|
apage = grab_cache_page(NODE_MAPPING(sbi), nid);
|
||||||
if (!apage)
|
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.
|
* 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 f2fs_sb_info *sbi = F2FS_P_SB(parent);
|
||||||
struct blk_plug plug;
|
struct blk_plug plug;
|
||||||
@ -1083,7 +1121,7 @@ void ra_node_pages(struct page *parent, int start)
|
|||||||
blk_finish_plug(&plug);
|
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 *parent, int start)
|
||||||
{
|
{
|
||||||
struct page *page;
|
struct page *page;
|
||||||
@ -1154,19 +1192,57 @@ void sync_inode_page(struct dnode_of_data *dn)
|
|||||||
dn->node_changed = ret ? true: false;
|
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,
|
int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino,
|
||||||
struct writeback_control *wbc)
|
struct writeback_control *wbc)
|
||||||
{
|
{
|
||||||
pgoff_t index, end;
|
pgoff_t index, end;
|
||||||
struct pagevec pvec;
|
struct pagevec pvec;
|
||||||
int step = ino ? 2 : 0;
|
int step = ino ? 2 : 0;
|
||||||
int nwritten = 0, wrote = 0;
|
int nwritten = 0;
|
||||||
|
|
||||||
pagevec_init(&pvec, 0);
|
pagevec_init(&pvec, 0);
|
||||||
|
|
||||||
next_step:
|
next_step:
|
||||||
index = 0;
|
index = 0;
|
||||||
end = LONG_MAX;
|
end = ULONG_MAX;
|
||||||
|
|
||||||
while (index <= end) {
|
while (index <= end) {
|
||||||
int i, nr_pages;
|
int i, nr_pages;
|
||||||
@ -1203,6 +1279,7 @@ next_step:
|
|||||||
* If an fsync mode,
|
* If an fsync mode,
|
||||||
* we should not skip writing node pages.
|
* we should not skip writing node pages.
|
||||||
*/
|
*/
|
||||||
|
lock_node:
|
||||||
if (ino && ino_of_node(page) == ino)
|
if (ino && ino_of_node(page) == ino)
|
||||||
lock_page(page);
|
lock_page(page);
|
||||||
else if (!trylock_page(page))
|
else if (!trylock_page(page))
|
||||||
@ -1221,6 +1298,17 @@ continue_unlock:
|
|||||||
goto 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))
|
if (!clear_page_dirty_for_io(page))
|
||||||
goto continue_unlock;
|
goto continue_unlock;
|
||||||
|
|
||||||
@ -1238,8 +1326,6 @@ continue_unlock:
|
|||||||
|
|
||||||
if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc))
|
if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc))
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
else
|
|
||||||
wrote++;
|
|
||||||
|
|
||||||
if (--wbc->nr_to_write == 0)
|
if (--wbc->nr_to_write == 0)
|
||||||
break;
|
break;
|
||||||
@ -1257,15 +1343,12 @@ continue_unlock:
|
|||||||
step++;
|
step++;
|
||||||
goto next_step;
|
goto next_step;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrote)
|
|
||||||
f2fs_submit_merged_bio(sbi, NODE, WRITE);
|
|
||||||
return nwritten;
|
return nwritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino)
|
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;
|
struct pagevec pvec;
|
||||||
int ret2 = 0, ret = 0;
|
int ret2 = 0, ret = 0;
|
||||||
|
|
||||||
@ -1287,7 +1370,7 @@ int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (ino && ino_of_node(page) == ino) {
|
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))
|
if (TestClearPageError(page))
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
}
|
}
|
||||||
@ -1326,8 +1409,6 @@ static int f2fs_write_node_page(struct page *page,
|
|||||||
if (unlikely(f2fs_cp_error(sbi)))
|
if (unlikely(f2fs_cp_error(sbi)))
|
||||||
goto redirty_out;
|
goto redirty_out;
|
||||||
|
|
||||||
f2fs_wait_on_page_writeback(page, NODE);
|
|
||||||
|
|
||||||
/* get old block addr of this node page */
|
/* get old block addr of this node page */
|
||||||
nid = nid_of_node(page);
|
nid = nid_of_node(page);
|
||||||
f2fs_bug_on(sbi, page->index != nid);
|
f2fs_bug_on(sbi, page->index != nid);
|
||||||
@ -1351,14 +1432,18 @@ static int f2fs_write_node_page(struct page *page,
|
|||||||
}
|
}
|
||||||
|
|
||||||
set_page_writeback(page);
|
set_page_writeback(page);
|
||||||
fio.blk_addr = ni.blk_addr;
|
fio.old_blkaddr = ni.blk_addr;
|
||||||
write_node_page(nid, &fio);
|
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);
|
dec_page_count(sbi, F2FS_DIRTY_NODES);
|
||||||
up_read(&sbi->node_write);
|
up_read(&sbi->node_write);
|
||||||
|
|
||||||
|
if (wbc->for_reclaim)
|
||||||
|
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, NODE, WRITE);
|
||||||
|
|
||||||
unlock_page(page);
|
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);
|
f2fs_submit_merged_bio(sbi, NODE, WRITE);
|
||||||
|
|
||||||
return 0;
|
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);
|
struct f2fs_sb_info *sbi = F2FS_M_SB(mapping);
|
||||||
long diff;
|
long diff;
|
||||||
|
|
||||||
trace_f2fs_writepages(mapping->host, wbc, NODE);
|
|
||||||
|
|
||||||
/* balancing f2fs's metadata in background */
|
/* balancing f2fs's metadata in background */
|
||||||
f2fs_balance_fs_bg(sbi);
|
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))
|
if (get_pages(sbi, F2FS_DIRTY_NODES) < nr_pages_to_skip(sbi, NODE))
|
||||||
goto skip_write;
|
goto skip_write;
|
||||||
|
|
||||||
|
trace_f2fs_writepages(mapping->host, wbc, NODE);
|
||||||
|
|
||||||
diff = nr_pages_to_write(sbi, NODE, wbc);
|
diff = nr_pages_to_write(sbi, NODE, wbc);
|
||||||
wbc->sync_mode = WB_SYNC_NONE;
|
wbc->sync_mode = WB_SYNC_NONE;
|
||||||
sync_node_pages(sbi, 0, wbc);
|
sync_node_pages(sbi, 0, wbc);
|
||||||
@ -1391,6 +1476,7 @@ static int f2fs_write_node_pages(struct address_space *mapping,
|
|||||||
|
|
||||||
skip_write:
|
skip_write:
|
||||||
wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_NODES);
|
wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_NODES);
|
||||||
|
trace_f2fs_writepages(mapping->host, wbc, NODE);
|
||||||
return 0;
|
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 f2fs_nm_info *nm_i = NM_I(sbi);
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
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;
|
int i = 0;
|
||||||
nid_t nid = nm_i->next_scan_nid;
|
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;
|
nm_i->next_scan_nid = nid;
|
||||||
|
|
||||||
/* find free nids from current sum_pages */
|
/* find free nids from current sum_pages */
|
||||||
mutex_lock(&curseg->curseg_mutex);
|
down_read(&curseg->journal_rwsem);
|
||||||
for (i = 0; i < nats_in_cursum(sum); i++) {
|
for (i = 0; i < nats_in_cursum(journal); i++) {
|
||||||
block_t addr = le32_to_cpu(nat_in_journal(sum, i).block_addr);
|
block_t addr;
|
||||||
nid = le32_to_cpu(nid_in_journal(sum, i));
|
|
||||||
|
addr = le32_to_cpu(nat_in_journal(journal, i).block_addr);
|
||||||
|
nid = le32_to_cpu(nid_in_journal(journal, i));
|
||||||
if (addr == NULL_ADDR)
|
if (addr == NULL_ADDR)
|
||||||
add_free_nid(sbi, nid, true);
|
add_free_nid(sbi, nid, true);
|
||||||
else
|
else
|
||||||
remove_free_nid(nm_i, nid);
|
remove_free_nid(nm_i, nid);
|
||||||
}
|
}
|
||||||
mutex_unlock(&curseg->curseg_mutex);
|
up_read(&curseg->journal_rwsem);
|
||||||
up_read(&nm_i->nat_tree_lock);
|
up_read(&nm_i->nat_tree_lock);
|
||||||
|
|
||||||
ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid),
|
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);
|
src_addr = inline_xattr_addr(page);
|
||||||
inline_size = inline_xattr_size(inode);
|
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);
|
memcpy(dst_addr, src_addr, inline_size);
|
||||||
update_inode:
|
update_inode:
|
||||||
update_inode(inode, ipage);
|
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 f2fs_nm_info *nm_i = NM_I(sbi);
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
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;
|
int i;
|
||||||
|
|
||||||
mutex_lock(&curseg->curseg_mutex);
|
down_write(&curseg->journal_rwsem);
|
||||||
for (i = 0; i < nats_in_cursum(sum); i++) {
|
for (i = 0; i < nats_in_cursum(journal); i++) {
|
||||||
struct nat_entry *ne;
|
struct nat_entry *ne;
|
||||||
struct f2fs_nat_entry raw_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);
|
ne = __lookup_nat_cache(nm_i, nid);
|
||||||
if (!ne) {
|
if (!ne) {
|
||||||
@ -1849,8 +1937,8 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi)
|
|||||||
}
|
}
|
||||||
__set_nat_cache_dirty(nm_i, ne);
|
__set_nat_cache_dirty(nm_i, ne);
|
||||||
}
|
}
|
||||||
update_nats_in_cursum(sum, -i);
|
update_nats_in_cursum(journal, -i);
|
||||||
mutex_unlock(&curseg->curseg_mutex);
|
up_write(&curseg->journal_rwsem);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __adjust_nat_entry_set(struct nat_entry_set *nes,
|
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 nat_entry_set *set)
|
||||||
{
|
{
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
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;
|
nid_t start_nid = set->set * NAT_ENTRY_PER_BLOCK;
|
||||||
bool to_journal = true;
|
bool to_journal = true;
|
||||||
struct f2fs_nat_block *nat_blk;
|
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.
|
* #1, flush nat entries to journal in current hot data summary block.
|
||||||
* #2, flush nat entries to nat page.
|
* #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;
|
to_journal = false;
|
||||||
|
|
||||||
if (to_journal) {
|
if (to_journal) {
|
||||||
mutex_lock(&curseg->curseg_mutex);
|
down_write(&curseg->journal_rwsem);
|
||||||
} else {
|
} else {
|
||||||
page = get_next_nat_page(sbi, start_nid);
|
page = get_next_nat_page(sbi, start_nid);
|
||||||
nat_blk = page_address(page);
|
nat_blk = page_address(page);
|
||||||
@ -1908,11 +1996,11 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (to_journal) {
|
if (to_journal) {
|
||||||
offset = lookup_journal_in_cursum(sum,
|
offset = lookup_journal_in_cursum(journal,
|
||||||
NAT_JOURNAL, nid, 1);
|
NAT_JOURNAL, nid, 1);
|
||||||
f2fs_bug_on(sbi, offset < 0);
|
f2fs_bug_on(sbi, offset < 0);
|
||||||
raw_ne = &nat_in_journal(sum, offset);
|
raw_ne = &nat_in_journal(journal, offset);
|
||||||
nid_in_journal(sum, offset) = cpu_to_le32(nid);
|
nid_in_journal(journal, offset) = cpu_to_le32(nid);
|
||||||
} else {
|
} else {
|
||||||
raw_ne = &nat_blk->entries[nid - start_nid];
|
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)
|
if (to_journal)
|
||||||
mutex_unlock(&curseg->curseg_mutex);
|
up_write(&curseg->journal_rwsem);
|
||||||
else
|
else
|
||||||
f2fs_put_page(page, 1);
|
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 f2fs_nm_info *nm_i = NM_I(sbi);
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
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 *setvec[SETVEC_SIZE];
|
||||||
struct nat_entry_set *set, *tmp;
|
struct nat_entry_set *set, *tmp;
|
||||||
unsigned int found;
|
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
|
* entries, remove all entries from journal and merge them
|
||||||
* into nat entry set.
|
* 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);
|
remove_nats_in_journal(sbi);
|
||||||
|
|
||||||
while ((found = __gang_lookup_nat_set(nm_i,
|
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;
|
set_idx = setvec[found - 1]->set + 1;
|
||||||
for (idx = 0; idx < found; idx++)
|
for (idx = 0; idx < found; idx++)
|
||||||
__adjust_nat_entry_set(setvec[idx], &sets,
|
__adjust_nat_entry_set(setvec[idx], &sets,
|
||||||
MAX_NAT_JENTRIES(sum));
|
MAX_NAT_JENTRIES(journal));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* flush dirty nats in nat entry set */
|
/* 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->nat_cnt = 0;
|
||||||
nm_i->ram_thresh = DEF_RAM_THRESHOLD;
|
nm_i->ram_thresh = DEF_RAM_THRESHOLD;
|
||||||
nm_i->ra_nid_pages = DEF_RA_NID_PAGES;
|
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_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC);
|
||||||
INIT_LIST_HEAD(&nm_i->free_nid_list);
|
INIT_LIST_HEAD(&nm_i->free_nid_list);
|
||||||
|
@ -25,6 +25,9 @@
|
|||||||
/* control the memory footprint threshold (10MB per 1GB ram) */
|
/* control the memory footprint threshold (10MB per 1GB ram) */
|
||||||
#define DEF_RAM_THRESHOLD 10
|
#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 */
|
/* vector size for gang look-up from nat cache that consists of radix tree */
|
||||||
#define NATVEC_SIZE 64
|
#define NATVEC_SIZE 64
|
||||||
#define SETVEC_SIZE 32
|
#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;
|
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 {
|
enum mem_type {
|
||||||
FREE_NIDS, /* indicates the free nid list */
|
FREE_NIDS, /* indicates the free nid list */
|
||||||
NAT_ENTRIES, /* indicates the cached nat entry */
|
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);
|
struct f2fs_node *rn = F2FS_NODE(p);
|
||||||
|
|
||||||
f2fs_wait_on_page_writeback(p, NODE);
|
f2fs_wait_on_page_writeback(p, NODE, true);
|
||||||
|
|
||||||
if (i)
|
if (i)
|
||||||
rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid);
|
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_fsync_dnode(page) is_node(page, FSYNC_BIT_SHIFT)
|
||||||
#define is_dent_dnode(page) is_node(page, DENT_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)
|
static inline void set_cold_node(struct inode *inode, struct page *page)
|
||||||
{
|
{
|
||||||
struct f2fs_node *rn = F2FS_NODE(page);
|
struct f2fs_node *rn = F2FS_NODE(page);
|
||||||
|
@ -350,8 +350,7 @@ got_it:
|
|||||||
inode = dn->inode;
|
inode = dn->inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
bidx = start_bidx_of_node(offset, F2FS_I(inode)) +
|
bidx = start_bidx_of_node(offset, inode) + le16_to_cpu(sum.ofs_in_node);
|
||||||
le16_to_cpu(sum.ofs_in_node);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if inode page is locked, unlock temporarily, but its reference
|
* 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,
|
static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||||
struct page *page, block_t blkaddr)
|
struct page *page, block_t blkaddr)
|
||||||
{
|
{
|
||||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
|
||||||
unsigned int start, end;
|
|
||||||
struct dnode_of_data dn;
|
struct dnode_of_data dn;
|
||||||
struct node_info ni;
|
struct node_info ni;
|
||||||
|
unsigned int start, end;
|
||||||
int err = 0, recovered = 0;
|
int err = 0, recovered = 0;
|
||||||
|
|
||||||
/* step 1: recover xattr */
|
/* step 1: recover xattr */
|
||||||
@ -409,8 +407,8 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* step 3: recover data indices */
|
/* step 3: recover data indices */
|
||||||
start = start_bidx_of_node(ofs_of_node(page), fi);
|
start = start_bidx_of_node(ofs_of_node(page), inode);
|
||||||
end = start + ADDRS_PER_PAGE(page, fi);
|
end = start + ADDRS_PER_PAGE(page, inode);
|
||||||
|
|
||||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
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)
|
if (err)
|
||||||
goto out;
|
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);
|
get_node_info(sbi, dn.nid, &ni);
|
||||||
f2fs_bug_on(sbi, ni.ino != ino_of_node(page));
|
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 */
|
/* write dummy data page */
|
||||||
f2fs_replace_block(sbi, &dn, src, dest,
|
f2fs_replace_block(sbi, &dn, src, dest,
|
||||||
ni.version, false);
|
ni.version, false, false);
|
||||||
recovered++;
|
recovered++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,70 +191,145 @@ void register_inmem_page(struct inode *inode, struct page *page)
|
|||||||
trace_f2fs_register_inmem_page(page, INMEM);
|
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_sb_info *sbi = F2FS_I_SB(inode);
|
||||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||||
struct inmem_pages *cur, *tmp;
|
struct inmem_pages *cur, *tmp;
|
||||||
bool submit_bio = false;
|
|
||||||
struct f2fs_io_info fio = {
|
struct f2fs_io_info fio = {
|
||||||
.sbi = sbi,
|
.sbi = sbi,
|
||||||
.type = DATA,
|
.type = DATA,
|
||||||
.rw = WRITE_SYNC | REQ_PRIO,
|
.rw = WRITE_SYNC | REQ_PRIO,
|
||||||
.encrypted_page = NULL,
|
.encrypted_page = NULL,
|
||||||
};
|
};
|
||||||
|
bool submit_bio = false;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
/*
|
list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) {
|
||||||
* The abort is true only when f2fs_evict_inode is called.
|
struct page *page = cur->page;
|
||||||
* Basically, the f2fs_evict_inode doesn't produce any data writes, so
|
|
||||||
* that we don't need to call f2fs_balance_fs.
|
lock_page(page);
|
||||||
* Otherwise, f2fs_gc in f2fs_balance_fs can wait forever until this
|
if (page->mapping == inode->i_mapping) {
|
||||||
* inode becomes free by iget_locked in f2fs_iget.
|
trace_f2fs_commit_inmem_page(page, INMEM);
|
||||||
*/
|
|
||||||
if (!abort) {
|
set_page_dirty(page);
|
||||||
f2fs_balance_fs(sbi, true);
|
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||||
f2fs_lock_op(sbi);
|
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);
|
if (submit_bio)
|
||||||
list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) {
|
f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE);
|
||||||
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);
|
|
||||||
|
|
||||||
list_del(&cur->list);
|
if (!err)
|
||||||
kmem_cache_free(inmem_entry_slab, cur);
|
__revoke_inmem_pages(inode, revoke_list, false, false);
|
||||||
dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
|
|
||||||
|
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);
|
mutex_unlock(&fi->inmem_lock);
|
||||||
|
|
||||||
if (!abort) {
|
f2fs_unlock_op(sbi);
|
||||||
f2fs_unlock_op(sbi);
|
|
||||||
if (submit_bio)
|
|
||||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
|
||||||
}
|
|
||||||
return err;
|
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 */
|
/* checkpoint is the only way to shrink partial cached entries */
|
||||||
if (!available_free_memory(sbi, NAT_ENTRIES) ||
|
if (!available_free_memory(sbi, NAT_ENTRIES) ||
|
||||||
excess_prefree_segs(sbi) ||
|
|
||||||
!available_free_memory(sbi, INO_ENTRIES) ||
|
!available_free_memory(sbi, INO_ENTRIES) ||
|
||||||
|
excess_prefree_segs(sbi) ||
|
||||||
|
excess_dirty_nats(sbi) ||
|
||||||
(is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) {
|
(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);
|
sync_dirty_inodes(sbi, FILE_INODE);
|
||||||
|
blk_finish_plug(&plug);
|
||||||
|
}
|
||||||
f2fs_sync_fs(sbi->sb, true);
|
f2fs_sync_fs(sbi->sb, true);
|
||||||
stat_inc_bg_cp_count(sbi->stat_info);
|
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)
|
bool discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr)
|
||||||
{
|
{
|
||||||
int err = -ENOTSUPP;
|
int err = -EOPNOTSUPP;
|
||||||
|
|
||||||
if (test_opt(sbi, DISCARD)) {
|
if (test_opt(sbi, DISCARD)) {
|
||||||
struct seg_entry *se = get_seg_entry(sbi,
|
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);
|
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)
|
static int is_next_segment_free(struct f2fs_sb_info *sbi, int type)
|
||||||
{
|
{
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, 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)) {
|
if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) {
|
||||||
segno = find_next_zero_bit(free_i->free_segmap,
|
segno = find_next_zero_bit(free_i->free_segmap,
|
||||||
MAIN_SEGS(sbi), *newseg + 1);
|
(hint + 1) * sbi->segs_per_sec, *newseg + 1);
|
||||||
if (segno - *newseg < sbi->segs_per_sec -
|
if (segno < (hint + 1) * sbi->segs_per_sec)
|
||||||
(*newseg % sbi->segs_per_sec))
|
|
||||||
goto got_it;
|
goto got_it;
|
||||||
}
|
}
|
||||||
find_other_zone:
|
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);
|
int type = __get_segment_type(fio->page, fio->type);
|
||||||
|
|
||||||
allocate_data_block(fio->sbi, fio->page, fio->blk_addr,
|
allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
|
||||||
&fio->blk_addr, sum, type);
|
&fio->new_blkaddr, sum, type);
|
||||||
|
|
||||||
/* writeout dirty page into bdev */
|
/* writeout dirty page into bdev */
|
||||||
f2fs_submit_page_mbio(fio);
|
f2fs_submit_page_mbio(fio);
|
||||||
@ -1293,7 +1398,8 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page)
|
|||||||
.sbi = sbi,
|
.sbi = sbi,
|
||||||
.type = META,
|
.type = META,
|
||||||
.rw = WRITE_SYNC | REQ_META | REQ_PRIO,
|
.rw = WRITE_SYNC | REQ_META | REQ_PRIO,
|
||||||
.blk_addr = page->index,
|
.old_blkaddr = page->index,
|
||||||
|
.new_blkaddr = page->index,
|
||||||
.page = page,
|
.page = page,
|
||||||
.encrypted_page = NULL,
|
.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);
|
get_node_info(sbi, dn->nid, &ni);
|
||||||
set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
|
set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
|
||||||
do_write_page(&sum, fio);
|
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)
|
void rewrite_data_page(struct f2fs_io_info *fio)
|
||||||
{
|
{
|
||||||
|
fio->new_blkaddr = fio->old_blkaddr;
|
||||||
stat_inc_inplace_blocks(fio->sbi);
|
stat_inc_inplace_blocks(fio->sbi);
|
||||||
f2fs_submit_page_mbio(fio);
|
f2fs_submit_page_mbio(fio);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __f2fs_replace_block(struct f2fs_sb_info *sbi,
|
void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||||
struct f2fs_summary *sum,
|
|
||||||
block_t old_blkaddr, block_t new_blkaddr,
|
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 sit_info *sit_i = SIT_I(sbi);
|
||||||
struct curseg_info *curseg;
|
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);
|
curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr);
|
||||||
__add_sum_entry(sbi, type, sum);
|
__add_sum_entry(sbi, type, sum);
|
||||||
|
|
||||||
if (!recover_curseg)
|
if (!recover_curseg || recover_newaddr)
|
||||||
update_sit_entry(sbi, new_blkaddr, 1);
|
update_sit_entry(sbi, new_blkaddr, 1);
|
||||||
if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
|
if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
|
||||||
update_sit_entry(sbi, old_blkaddr, -1);
|
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,
|
void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
|
||||||
block_t old_addr, block_t new_addr,
|
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;
|
struct f2fs_summary sum;
|
||||||
|
|
||||||
set_summary(&sum, dn->nid, dn->ofs_in_node, version);
|
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;
|
f2fs_update_data_blkaddr(dn, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void f2fs_wait_on_page_writeback(struct page *page,
|
void f2fs_wait_on_page_writeback(struct page *page,
|
||||||
enum page_type type)
|
enum page_type type, bool ordered)
|
||||||
{
|
{
|
||||||
if (PageWriteback(page)) {
|
if (PageWriteback(page)) {
|
||||||
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
|
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
|
||||||
|
|
||||||
if (is_merged_page(sbi, page, type))
|
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, type, WRITE);
|
||||||
f2fs_submit_merged_bio(sbi, type, WRITE);
|
if (ordered)
|
||||||
wait_on_page_writeback(page);
|
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);
|
cpage = find_lock_page(META_MAPPING(sbi), blkaddr);
|
||||||
if (cpage) {
|
if (cpage) {
|
||||||
f2fs_wait_on_page_writeback(cpage, DATA);
|
f2fs_wait_on_page_writeback(cpage, DATA, true);
|
||||||
f2fs_put_page(cpage, 1);
|
f2fs_put_page(cpage, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1498,12 +1568,11 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi)
|
|||||||
|
|
||||||
/* Step 1: restore nat cache */
|
/* Step 1: restore nat cache */
|
||||||
seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
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 */
|
/* Step 2: restore sit cache */
|
||||||
seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
||||||
memcpy(&seg_i->sum_blk->n_sits, kaddr + SUM_JOURNAL_SIZE,
|
memcpy(seg_i->journal, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE);
|
||||||
SUM_JOURNAL_SIZE);
|
|
||||||
offset = 2 * SUM_JOURNAL_SIZE;
|
offset = 2 * SUM_JOURNAL_SIZE;
|
||||||
|
|
||||||
/* Step 3: restore summary entries */
|
/* 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 */
|
/* set uncompleted segment to curseg */
|
||||||
curseg = CURSEG_I(sbi, type);
|
curseg = CURSEG_I(sbi, type);
|
||||||
mutex_lock(&curseg->curseg_mutex);
|
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;
|
curseg->next_segno = segno;
|
||||||
reset_curseg(sbi, type, 0);
|
reset_curseg(sbi, type, 0);
|
||||||
curseg->alloc_type = ckpt->alloc_type[type];
|
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 */
|
/* Step 1: write nat cache */
|
||||||
seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
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;
|
written_size += SUM_JOURNAL_SIZE;
|
||||||
|
|
||||||
/* Step 2: write sit cache */
|
/* Step 2: write sit cache */
|
||||||
seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
||||||
memcpy(kaddr + written_size, &seg_i->sum_blk->n_sits,
|
memcpy(kaddr + written_size, seg_i->journal, SUM_JOURNAL_SIZE);
|
||||||
SUM_JOURNAL_SIZE);
|
|
||||||
written_size += SUM_JOURNAL_SIZE;
|
written_size += SUM_JOURNAL_SIZE;
|
||||||
|
|
||||||
/* Step 3: write summary entries */
|
/* Step 3: write summary entries */
|
||||||
@ -1706,12 +1781,8 @@ static void write_normal_summaries(struct f2fs_sb_info *sbi,
|
|||||||
else
|
else
|
||||||
end = type + NR_CURSEG_NODE_TYPE;
|
end = type + NR_CURSEG_NODE_TYPE;
|
||||||
|
|
||||||
for (i = type; i < end; i++) {
|
for (i = type; i < end; i++)
|
||||||
struct curseg_info *sum = CURSEG_I(sbi, i);
|
write_current_sum_page(sbi, i, blkaddr + (i - type));
|
||||||
mutex_lock(&sum->curseg_mutex);
|
|
||||||
write_sum_page(sbi, sum->sum_blk, blkaddr + (i - type));
|
|
||||||
mutex_unlock(&sum->curseg_mutex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk)
|
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);
|
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)
|
unsigned int val, int alloc)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (type == NAT_JOURNAL) {
|
if (type == NAT_JOURNAL) {
|
||||||
for (i = 0; i < nats_in_cursum(sum); i++) {
|
for (i = 0; i < nats_in_cursum(journal); i++) {
|
||||||
if (le32_to_cpu(nid_in_journal(sum, i)) == val)
|
if (le32_to_cpu(nid_in_journal(journal, i)) == val)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
if (alloc && __has_cursum_space(sum, 1, NAT_JOURNAL))
|
if (alloc && __has_cursum_space(journal, 1, NAT_JOURNAL))
|
||||||
return update_nats_in_cursum(sum, 1);
|
return update_nats_in_cursum(journal, 1);
|
||||||
} else if (type == SIT_JOURNAL) {
|
} else if (type == SIT_JOURNAL) {
|
||||||
for (i = 0; i < sits_in_cursum(sum); i++)
|
for (i = 0; i < sits_in_cursum(journal); i++)
|
||||||
if (le32_to_cpu(segno_in_journal(sum, i)) == val)
|
if (le32_to_cpu(segno_in_journal(journal, i)) == val)
|
||||||
return i;
|
return i;
|
||||||
if (alloc && __has_cursum_space(sum, 1, SIT_JOURNAL))
|
if (alloc && __has_cursum_space(journal, 1, SIT_JOURNAL))
|
||||||
return update_sits_in_cursum(sum, 1);
|
return update_sits_in_cursum(journal, 1);
|
||||||
}
|
}
|
||||||
return -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)
|
static void remove_sits_in_journal(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
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;
|
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;
|
unsigned int segno;
|
||||||
bool dirtied;
|
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);
|
dirtied = __mark_sit_entry_dirty(sbi, segno);
|
||||||
|
|
||||||
if (!dirtied)
|
if (!dirtied)
|
||||||
add_sit_entry(segno, &SM_I(sbi)->sit_entry_set);
|
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);
|
struct sit_info *sit_i = SIT_I(sbi);
|
||||||
unsigned long *bitmap = sit_i->dirty_sentries_bitmap;
|
unsigned long *bitmap = sit_i->dirty_sentries_bitmap;
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
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 sit_entry_set *ses, *tmp;
|
||||||
struct list_head *head = &SM_I(sbi)->sit_entry_set;
|
struct list_head *head = &SM_I(sbi)->sit_entry_set;
|
||||||
bool to_journal = true;
|
bool to_journal = true;
|
||||||
struct seg_entry *se;
|
struct seg_entry *se;
|
||||||
|
|
||||||
mutex_lock(&curseg->curseg_mutex);
|
|
||||||
mutex_lock(&sit_i->sentry_lock);
|
mutex_lock(&sit_i->sentry_lock);
|
||||||
|
|
||||||
if (!sit_i->dirty_sentries)
|
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
|
* entries, remove all entries from journal and add and account
|
||||||
* them in sit entry set.
|
* 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);
|
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;
|
unsigned int segno = start_segno;
|
||||||
|
|
||||||
if (to_journal &&
|
if (to_journal &&
|
||||||
!__has_cursum_space(sum, ses->entry_cnt, SIT_JOURNAL))
|
!__has_cursum_space(journal, ses->entry_cnt, SIT_JOURNAL))
|
||||||
to_journal = false;
|
to_journal = false;
|
||||||
|
|
||||||
if (!to_journal) {
|
if (to_journal) {
|
||||||
|
down_write(&curseg->journal_rwsem);
|
||||||
|
} else {
|
||||||
page = get_next_sit_page(sbi, start_segno);
|
page = get_next_sit_page(sbi, start_segno);
|
||||||
raw_sit = page_address(page);
|
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) {
|
if (to_journal) {
|
||||||
offset = lookup_journal_in_cursum(sum,
|
offset = lookup_journal_in_cursum(journal,
|
||||||
SIT_JOURNAL, segno, 1);
|
SIT_JOURNAL, segno, 1);
|
||||||
f2fs_bug_on(sbi, offset < 0);
|
f2fs_bug_on(sbi, offset < 0);
|
||||||
segno_in_journal(sum, offset) =
|
segno_in_journal(journal, offset) =
|
||||||
cpu_to_le32(segno);
|
cpu_to_le32(segno);
|
||||||
seg_info_to_raw_sit(se,
|
seg_info_to_raw_sit(se,
|
||||||
&sit_in_journal(sum, offset));
|
&sit_in_journal(journal, offset));
|
||||||
} else {
|
} else {
|
||||||
sit_offset = SIT_ENTRY_OFFSET(sit_i, segno);
|
sit_offset = SIT_ENTRY_OFFSET(sit_i, segno);
|
||||||
seg_info_to_raw_sit(se,
|
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--;
|
ses->entry_cnt--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!to_journal)
|
if (to_journal)
|
||||||
|
up_write(&curseg->journal_rwsem);
|
||||||
|
else
|
||||||
f2fs_put_page(page, 1);
|
f2fs_put_page(page, 1);
|
||||||
|
|
||||||
f2fs_bug_on(sbi, ses->entry_cnt);
|
f2fs_bug_on(sbi, ses->entry_cnt);
|
||||||
@ -1967,7 +2043,6 @@ out:
|
|||||||
add_discard_addrs(sbi, cpc);
|
add_discard_addrs(sbi, cpc);
|
||||||
}
|
}
|
||||||
mutex_unlock(&sit_i->sentry_lock);
|
mutex_unlock(&sit_i->sentry_lock);
|
||||||
mutex_unlock(&curseg->curseg_mutex);
|
|
||||||
|
|
||||||
set_prefree_as_free_segments(sbi);
|
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);
|
array[i].sum_blk = kzalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
|
||||||
if (!array[i].sum_blk)
|
if (!array[i].sum_blk)
|
||||||
return -ENOMEM;
|
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].segno = NULL_SEGNO;
|
||||||
array[i].next_blkoff = 0;
|
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 sit_info *sit_i = SIT_I(sbi);
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
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);
|
int sit_blk_cnt = SIT_BLK_CNT(sbi);
|
||||||
unsigned int i, start, end;
|
unsigned int i, start, end;
|
||||||
unsigned int readed, start_blk = 0;
|
unsigned int readed, start_blk = 0;
|
||||||
int nrpages = MAX_BIO_BLOCKS(sbi);
|
int nrpages = MAX_BIO_BLOCKS(sbi) * 8;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT, true);
|
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 f2fs_sit_entry sit;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
|
|
||||||
mutex_lock(&curseg->curseg_mutex);
|
down_read(&curseg->journal_rwsem);
|
||||||
for (i = 0; i < sits_in_cursum(sum); i++) {
|
for (i = 0; i < sits_in_cursum(journal); i++) {
|
||||||
if (le32_to_cpu(segno_in_journal(sum, i))
|
if (le32_to_cpu(segno_in_journal(journal, i))
|
||||||
== start) {
|
== start) {
|
||||||
sit = sit_in_journal(sum, i);
|
sit = sit_in_journal(journal, i);
|
||||||
mutex_unlock(&curseg->curseg_mutex);
|
up_read(&curseg->journal_rwsem);
|
||||||
goto got_it;
|
goto got_it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mutex_unlock(&curseg->curseg_mutex);
|
up_read(&curseg->journal_rwsem);
|
||||||
|
|
||||||
page = get_current_sit_page(sbi, start);
|
page = get_current_sit_page(sbi, start);
|
||||||
sit_blk = (struct f2fs_sit_block *)page_address(page);
|
sit_blk = (struct f2fs_sit_block *)page_address(page);
|
||||||
@ -2371,8 +2451,10 @@ static void destroy_curseg(struct f2fs_sb_info *sbi)
|
|||||||
if (!array)
|
if (!array)
|
||||||
return;
|
return;
|
||||||
SM_I(sbi)->curseg_array = NULL;
|
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].sum_blk);
|
||||||
|
kfree(array[i].journal);
|
||||||
|
}
|
||||||
kfree(array);
|
kfree(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ struct segment_allocation {
|
|||||||
* this value is set in page as a private data which indicate that
|
* 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.
|
* 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) \
|
#define IS_ATOMIC_WRITTEN_PAGE(page) \
|
||||||
(page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE)
|
(page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE)
|
||||||
@ -191,6 +191,7 @@ struct segment_allocation {
|
|||||||
struct inmem_pages {
|
struct inmem_pages {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
|
block_t old_addr; /* for revoking when fail to commit */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sit_info {
|
struct sit_info {
|
||||||
@ -257,6 +258,8 @@ struct victim_selection {
|
|||||||
struct curseg_info {
|
struct curseg_info {
|
||||||
struct mutex curseg_mutex; /* lock for consistency */
|
struct mutex curseg_mutex; /* lock for consistency */
|
||||||
struct f2fs_summary_block *sum_blk; /* cached summary block */
|
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 char alloc_type; /* current allocation type */
|
||||||
unsigned int segno; /* current segment number */
|
unsigned int segno; /* current segment number */
|
||||||
unsigned short next_blkoff; /* next block offset to write */
|
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;
|
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,
|
static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
|
||||||
struct f2fs_sb_info *sbi, char *buf)
|
struct f2fs_sb_info *sbi, char *buf)
|
||||||
{
|
{
|
||||||
@ -204,6 +217,9 @@ static struct f2fs_attr f2fs_attr_##_name = { \
|
|||||||
f2fs_sbi_show, f2fs_sbi_store, \
|
f2fs_sbi_show, f2fs_sbi_store, \
|
||||||
offsetof(struct struct_name, elname))
|
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_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_max_sleep_time, max_sleep_time);
|
||||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_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(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, 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, 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, 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, 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, cp_interval, interval_time[CP_TIME]);
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_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)
|
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
|
||||||
static struct attribute *f2fs_attrs[] = {
|
static struct attribute *f2fs_attrs[] = {
|
||||||
@ -237,8 +255,10 @@ static struct attribute *f2fs_attrs[] = {
|
|||||||
ATTR_LIST(dir_level),
|
ATTR_LIST(dir_level),
|
||||||
ATTR_LIST(ram_thresh),
|
ATTR_LIST(ram_thresh),
|
||||||
ATTR_LIST(ra_nid_pages),
|
ATTR_LIST(ra_nid_pages),
|
||||||
|
ATTR_LIST(dirty_nats_ratio),
|
||||||
ATTR_LIST(cp_interval),
|
ATTR_LIST(cp_interval),
|
||||||
ATTR_LIST(idle_interval),
|
ATTR_LIST(idle_interval),
|
||||||
|
ATTR_LIST(lifetime_write_kbytes),
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -450,10 +470,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
|
|||||||
|
|
||||||
/* Will be used by directory only */
|
/* Will be used by directory only */
|
||||||
fi->i_dir_level = F2FS_SB(sb)->dir_level;
|
fi->i_dir_level = F2FS_SB(sb)->dir_level;
|
||||||
|
|
||||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
|
||||||
fi->i_crypt_info = NULL;
|
|
||||||
#endif
|
|
||||||
return &fi->vfs_inode;
|
return &fi->vfs_inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,7 +490,7 @@ static int f2fs_drop_inode(struct inode *inode)
|
|||||||
|
|
||||||
/* some remained atomic pages should discarded */
|
/* some remained atomic pages should discarded */
|
||||||
if (f2fs_is_atomic_file(inode))
|
if (f2fs_is_atomic_file(inode))
|
||||||
commit_inmem_pages(inode, true);
|
drop_inmem_pages(inode);
|
||||||
|
|
||||||
/* should remain fi->extent_tree for writepage */
|
/* should remain fi->extent_tree for writepage */
|
||||||
f2fs_destroy_extent_node(inode);
|
f2fs_destroy_extent_node(inode);
|
||||||
@ -487,11 +503,7 @@ static int f2fs_drop_inode(struct inode *inode)
|
|||||||
|
|
||||||
sb_end_intwrite(inode->i_sb);
|
sb_end_intwrite(inode->i_sb);
|
||||||
|
|
||||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
fscrypt_put_encryption_info(inode, NULL);
|
||||||
if (F2FS_I(inode)->i_crypt_info)
|
|
||||||
f2fs_free_encryption_info(inode,
|
|
||||||
F2FS_I(inode)->i_crypt_info);
|
|
||||||
#endif
|
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
atomic_dec(&inode->i_count);
|
atomic_dec(&inode->i_count);
|
||||||
}
|
}
|
||||||
@ -562,6 +574,10 @@ static void f2fs_put_super(struct super_block *sb)
|
|||||||
f2fs_leave_shrinker(sbi);
|
f2fs_leave_shrinker(sbi);
|
||||||
mutex_unlock(&sbi->umount_mutex);
|
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->node_inode);
|
||||||
iput(sbi->meta_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);
|
wait_for_completion(&sbi->s_kobj_unregister);
|
||||||
|
|
||||||
sb->s_fs_info = NULL;
|
sb->s_fs_info = NULL;
|
||||||
|
if (sbi->s_chksum_driver)
|
||||||
|
crypto_free_shash(sbi->s_chksum_driver);
|
||||||
kfree(sbi->raw_super);
|
kfree(sbi->raw_super);
|
||||||
kfree(sbi);
|
kfree(sbi);
|
||||||
}
|
}
|
||||||
@ -766,8 +784,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|||||||
bool need_stop_gc = false;
|
bool need_stop_gc = false;
|
||||||
bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
|
bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
|
||||||
|
|
||||||
sync_filesystem(sb);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save the old mount options in case we
|
* Save the old mount options in case we
|
||||||
* need to restore them.
|
* 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;
|
org_mount_opt = sbi->mount_opt;
|
||||||
active_logs = sbi->active_logs;
|
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;
|
sbi->mount_opt.opt = 0;
|
||||||
default_options(sbi);
|
default_options(sbi);
|
||||||
|
|
||||||
@ -862,6 +885,41 @@ static struct super_operations f2fs_sops = {
|
|||||||
.remount_fs = f2fs_remount,
|
.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,
|
static struct inode *f2fs_nfs_get_inode(struct super_block *sb,
|
||||||
u64 ino, u32 generation)
|
u64 ino, u32 generation)
|
||||||
{
|
{
|
||||||
@ -1074,7 +1132,7 @@ static int sanity_check_raw_super(struct super_block *sb,
|
|||||||
return 0;
|
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;
|
unsigned int total, fsmeta;
|
||||||
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
|
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.
|
* Read f2fs raw super block.
|
||||||
* Because we have two copies of super block, so read the first one at first,
|
* Because we have two copies of super block, so read both of them
|
||||||
* if the first one is invalid, move to read the second one.
|
* 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,
|
static int read_raw_super_block(struct super_block *sb,
|
||||||
struct f2fs_super_block **raw_super,
|
struct f2fs_super_block **raw_super,
|
||||||
int *valid_super_block, int *recovery)
|
int *valid_super_block, int *recovery)
|
||||||
{
|
{
|
||||||
int block = 0;
|
int block;
|
||||||
struct buffer_head *bh;
|
struct buffer_head *bh;
|
||||||
struct f2fs_super_block *super, *buf;
|
struct f2fs_super_block *super, *buf;
|
||||||
int err = 0;
|
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);
|
super = kzalloc(sizeof(struct f2fs_super_block), GFP_KERNEL);
|
||||||
if (!super)
|
if (!super)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
retry:
|
|
||||||
bh = sb_bread(sb, block);
|
for (block = 0; block < 2; block++) {
|
||||||
if (!bh) {
|
bh = sb_bread(sb, block);
|
||||||
*recovery = 1;
|
if (!bh) {
|
||||||
f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock",
|
f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock",
|
||||||
block + 1);
|
block + 1);
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
goto next;
|
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 */
|
/* sanity checking of raw super */
|
||||||
if (sanity_check_raw_super(sb, buf)) {
|
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);
|
brelse(bh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fail to read any one of the superblocks*/
|
||||||
|
if (err < 0)
|
||||||
*recovery = 1;
|
*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 */
|
/* No valid superblock */
|
||||||
if (!*raw_super) {
|
if (!*raw_super)
|
||||||
kfree(super);
|
kfree(super);
|
||||||
return err;
|
else
|
||||||
}
|
err = 0;
|
||||||
|
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __f2fs_commit_super(struct f2fs_sb_info *sbi, int block)
|
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;
|
bool retry = true, need_fsck = false;
|
||||||
char *options = NULL;
|
char *options = NULL;
|
||||||
int recovery, i, valid_super_block;
|
int recovery, i, valid_super_block;
|
||||||
|
struct curseg_info *seg_i;
|
||||||
|
|
||||||
try_onemore:
|
try_onemore:
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
@ -1254,6 +1312,15 @@ try_onemore:
|
|||||||
if (!sbi)
|
if (!sbi)
|
||||||
return -ENOMEM;
|
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 */
|
/* set a block size */
|
||||||
if (unlikely(!sb_set_blocksize(sb, F2FS_BLKSIZE))) {
|
if (unlikely(!sb_set_blocksize(sb, F2FS_BLKSIZE))) {
|
||||||
f2fs_msg(sb, KERN_ERR, "unable to set blocksize");
|
f2fs_msg(sb, KERN_ERR, "unable to set blocksize");
|
||||||
@ -1285,6 +1352,7 @@ try_onemore:
|
|||||||
get_random_bytes(&sbi->s_next_generation, sizeof(u32));
|
get_random_bytes(&sbi->s_next_generation, sizeof(u32));
|
||||||
|
|
||||||
sb->s_op = &f2fs_sops;
|
sb->s_op = &f2fs_sops;
|
||||||
|
sb->s_cop = &f2fs_cryptops;
|
||||||
sb->s_xattr = f2fs_xattr_handlers;
|
sb->s_xattr = f2fs_xattr_handlers;
|
||||||
sb->s_export_op = &f2fs_export_ops;
|
sb->s_export_op = &f2fs_export_ops;
|
||||||
sb->s_magic = F2FS_SUPER_MAGIC;
|
sb->s_magic = F2FS_SUPER_MAGIC;
|
||||||
@ -1333,13 +1401,6 @@ try_onemore:
|
|||||||
goto free_meta_inode;
|
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 =
|
sbi->total_valid_node_count =
|
||||||
le32_to_cpu(sbi->ckpt->valid_node_count);
|
le32_to_cpu(sbi->ckpt->valid_node_count);
|
||||||
sbi->total_valid_inode_count =
|
sbi->total_valid_inode_count =
|
||||||
@ -1372,6 +1433,17 @@ try_onemore:
|
|||||||
goto free_nm;
|
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);
|
build_gc_manager(sbi);
|
||||||
|
|
||||||
/* get an inode for node space */
|
/* get an inode for node space */
|
||||||
@ -1466,8 +1538,10 @@ try_onemore:
|
|||||||
|
|
||||||
/* recover broken superblock */
|
/* recover broken superblock */
|
||||||
if (recovery && !f2fs_readonly(sb) && !bdev_read_only(sb->s_bdev)) {
|
if (recovery && !f2fs_readonly(sb) && !bdev_read_only(sb->s_bdev)) {
|
||||||
f2fs_msg(sb, KERN_INFO, "Recover invalid superblock");
|
err = f2fs_commit_super(sbi, true);
|
||||||
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);
|
f2fs_update_time(sbi, CP_TIME);
|
||||||
@ -1496,7 +1570,6 @@ free_nm:
|
|||||||
destroy_node_manager(sbi);
|
destroy_node_manager(sbi);
|
||||||
free_sm:
|
free_sm:
|
||||||
destroy_segment_manager(sbi);
|
destroy_segment_manager(sbi);
|
||||||
free_cp:
|
|
||||||
kfree(sbi->ckpt);
|
kfree(sbi->ckpt);
|
||||||
free_meta_inode:
|
free_meta_inode:
|
||||||
make_bad_inode(sbi->meta_inode);
|
make_bad_inode(sbi->meta_inode);
|
||||||
@ -1506,6 +1579,8 @@ free_options:
|
|||||||
free_sb_buf:
|
free_sb_buf:
|
||||||
kfree(raw_super);
|
kfree(raw_super);
|
||||||
free_sbi:
|
free_sbi:
|
||||||
|
if (sbi->s_chksum_driver)
|
||||||
|
crypto_free_shash(sbi->s_chksum_driver);
|
||||||
kfree(sbi);
|
kfree(sbi);
|
||||||
|
|
||||||
/* give only one another chance */
|
/* give only one another chance */
|
||||||
@ -1585,13 +1660,9 @@ static int __init init_f2fs_fs(void)
|
|||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto free_extent_cache;
|
goto free_extent_cache;
|
||||||
}
|
}
|
||||||
err = f2fs_init_crypto();
|
|
||||||
if (err)
|
|
||||||
goto free_kset;
|
|
||||||
|
|
||||||
err = register_shrinker(&f2fs_shrinker_info);
|
err = register_shrinker(&f2fs_shrinker_info);
|
||||||
if (err)
|
if (err)
|
||||||
goto free_crypto;
|
goto free_kset;
|
||||||
|
|
||||||
err = register_filesystem(&f2fs_fs_type);
|
err = register_filesystem(&f2fs_fs_type);
|
||||||
if (err)
|
if (err)
|
||||||
@ -1606,8 +1677,6 @@ free_filesystem:
|
|||||||
unregister_filesystem(&f2fs_fs_type);
|
unregister_filesystem(&f2fs_fs_type);
|
||||||
free_shrinker:
|
free_shrinker:
|
||||||
unregister_shrinker(&f2fs_shrinker_info);
|
unregister_shrinker(&f2fs_shrinker_info);
|
||||||
free_crypto:
|
|
||||||
f2fs_exit_crypto();
|
|
||||||
free_kset:
|
free_kset:
|
||||||
kset_unregister(f2fs_kset);
|
kset_unregister(f2fs_kset);
|
||||||
free_extent_cache:
|
free_extent_cache:
|
||||||
@ -1630,7 +1699,6 @@ static void __exit exit_f2fs_fs(void)
|
|||||||
f2fs_destroy_root_stats();
|
f2fs_destroy_root_stats();
|
||||||
unregister_shrinker(&f2fs_shrinker_info);
|
unregister_shrinker(&f2fs_shrinker_info);
|
||||||
unregister_filesystem(&f2fs_fs_type);
|
unregister_filesystem(&f2fs_fs_type);
|
||||||
f2fs_exit_crypto();
|
|
||||||
destroy_extent_cache();
|
destroy_extent_cache();
|
||||||
destroy_checkpoint_caches();
|
destroy_checkpoint_caches();
|
||||||
destroy_segment_manager_caches();
|
destroy_segment_manager_caches();
|
||||||
|
@ -29,7 +29,8 @@ static inline void __print_last_io(void)
|
|||||||
last_io.major, last_io.minor,
|
last_io.major, last_io.minor,
|
||||||
last_io.pid, "----------------",
|
last_io.pid, "----------------",
|
||||||
last_io.type,
|
last_io.type,
|
||||||
last_io.fio.rw, last_io.fio.blk_addr,
|
last_io.fio.rw,
|
||||||
|
last_io.fio.new_blkaddr,
|
||||||
last_io.len);
|
last_io.len);
|
||||||
memset(&last_io, 0, sizeof(last_io));
|
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.pid == pid &&
|
||||||
last_io.type == __file_type(inode, pid) &&
|
last_io.type == __file_type(inode, pid) &&
|
||||||
last_io.fio.rw == fio->rw &&
|
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++;
|
last_io.len++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -300,7 +300,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||||||
|
|
||||||
if (ipage) {
|
if (ipage) {
|
||||||
inline_addr = inline_xattr_addr(ipage);
|
inline_addr = inline_xattr_addr(ipage);
|
||||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||||
} else {
|
} else {
|
||||||
page = get_node_page(sbi, inode->i_ino);
|
page = get_node_page(sbi, inode->i_ino);
|
||||||
if (IS_ERR(page)) {
|
if (IS_ERR(page)) {
|
||||||
@ -308,7 +308,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||||||
return PTR_ERR(page);
|
return PTR_ERR(page);
|
||||||
}
|
}
|
||||||
inline_addr = inline_xattr_addr(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);
|
memcpy(inline_addr, txattr_addr, inline_size);
|
||||||
f2fs_put_page(page, 1);
|
f2fs_put_page(page, 1);
|
||||||
@ -329,7 +329,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||||||
return PTR_ERR(xpage);
|
return PTR_ERR(xpage);
|
||||||
}
|
}
|
||||||
f2fs_bug_on(sbi, new_nid);
|
f2fs_bug_on(sbi, new_nid);
|
||||||
f2fs_wait_on_page_writeback(xpage, NODE);
|
f2fs_wait_on_page_writeback(xpage, NODE, true);
|
||||||
} else {
|
} else {
|
||||||
struct dnode_of_data dn;
|
struct dnode_of_data dn;
|
||||||
set_new_dnode(&dn, inode, NULL, NULL, new_nid);
|
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
|
#define f2fs_xattr_handlers NULL
|
||||||
static inline int f2fs_setxattr(struct inode *inode, int index,
|
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;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
@ -228,6 +228,8 @@ struct dentry_operations {
|
|||||||
#define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */
|
#define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */
|
||||||
#define DCACHE_OP_SELECT_INODE 0x02000000 /* Unioned entry: dcache op selects inode */
|
#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;
|
extern seqlock_t rename_lock;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#define F2FS_BLKSIZE 4096 /* support only 4KB block */
|
#define F2FS_BLKSIZE 4096 /* support only 4KB block */
|
||||||
#define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */
|
#define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */
|
||||||
#define F2FS_MAX_EXTENSION 64 /* # of extension entries */
|
#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 NULL_ADDR ((block_t)0) /* used as block_t addresses */
|
||||||
#define NEW_ADDR ((block_t)-1) /* 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 F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */
|
||||||
#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
|
#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
|
||||||
#define DEF_NIDS_PER_INODE 5 /* Node IDs 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 ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
|
||||||
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
|
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
|
||||||
|
|
||||||
#define ADDRS_PER_PAGE(page, fi) \
|
#define ADDRS_PER_PAGE(page, inode) \
|
||||||
(IS_INODE(page) ? ADDRS_PER_INODE(fi) : ADDRS_PER_BLOCK)
|
(IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK)
|
||||||
|
|
||||||
#define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1)
|
#define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1)
|
||||||
#define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2)
|
#define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2)
|
||||||
@ -345,7 +345,7 @@ struct f2fs_summary {
|
|||||||
|
|
||||||
struct summary_footer {
|
struct summary_footer {
|
||||||
unsigned char entry_type; /* SUM_TYPE_XXX */
|
unsigned char entry_type; /* SUM_TYPE_XXX */
|
||||||
__u32 check_sum; /* summary checksum */
|
__le32 check_sum; /* summary checksum */
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
#define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\
|
#define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\
|
||||||
@ -358,6 +358,12 @@ struct summary_footer {
|
|||||||
sizeof(struct sit_journal_entry))
|
sizeof(struct sit_journal_entry))
|
||||||
#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\
|
#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\
|
||||||
sizeof(struct sit_journal_entry))
|
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
|
* frequently updated NAT/SIT entries can be stored in the spare area in
|
||||||
* summary blocks
|
* summary blocks
|
||||||
@ -387,18 +393,28 @@ struct sit_journal {
|
|||||||
__u8 reserved[SIT_JOURNAL_RESERVED];
|
__u8 reserved[SIT_JOURNAL_RESERVED];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
/* 4KB-sized summary block structure */
|
struct f2fs_extra_info {
|
||||||
struct f2fs_summary_block {
|
__le64 kbytes_written;
|
||||||
struct f2fs_summary entries[ENTRIES_IN_SUM];
|
__u8 reserved[EXTRA_INFO_RESERVED];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct f2fs_journal {
|
||||||
union {
|
union {
|
||||||
__le16 n_nats;
|
__le16 n_nats;
|
||||||
__le16 n_sits;
|
__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 {
|
union {
|
||||||
struct nat_journal nat_j;
|
struct nat_journal nat_j;
|
||||||
struct sit_journal sit_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;
|
struct summary_footer footer;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
@ -53,6 +53,8 @@ struct swap_info_struct;
|
|||||||
struct seq_file;
|
struct seq_file;
|
||||||
struct workqueue_struct;
|
struct workqueue_struct;
|
||||||
struct iov_iter;
|
struct iov_iter;
|
||||||
|
struct fscrypt_info;
|
||||||
|
struct fscrypt_operations;
|
||||||
|
|
||||||
extern void __init inode_init(void);
|
extern void __init inode_init(void);
|
||||||
extern void __init inode_init_early(void);
|
extern void __init inode_init_early(void);
|
||||||
@ -679,6 +681,10 @@ struct inode {
|
|||||||
struct hlist_head i_fsnotify_marks;
|
struct hlist_head i_fsnotify_marks;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
|
||||||
|
struct fscrypt_info *i_crypt_info;
|
||||||
|
#endif
|
||||||
|
|
||||||
void *i_private; /* fs or device private pointer */
|
void *i_private; /* fs or device private pointer */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1324,6 +1330,8 @@ struct super_block {
|
|||||||
#endif
|
#endif
|
||||||
const struct xattr_handler **s_xattr;
|
const struct xattr_handler **s_xattr;
|
||||||
|
|
||||||
|
const struct fscrypt_operations *s_cop;
|
||||||
|
|
||||||
struct hlist_bl_head s_anon; /* anonymous dentries for (nfs) exporting */
|
struct hlist_bl_head s_anon; /* anonymous dentries for (nfs) exporting */
|
||||||
struct list_head s_mounts; /* list of mounts; _not_ for fs use */
|
struct list_head s_mounts; /* list of mounts; _not_ for fs use */
|
||||||
struct block_device *s_bdev;
|
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" }, \
|
{ META_FLUSH, "META_FLUSH" }, \
|
||||||
{ INMEM, "INMEM" }, \
|
{ INMEM, "INMEM" }, \
|
||||||
{ INMEM_DROP, "INMEM_DROP" }, \
|
{ INMEM_DROP, "INMEM_DROP" }, \
|
||||||
|
{ INMEM_REVOKE, "INMEM_REVOKE" }, \
|
||||||
{ IPU, "IN-PLACE" }, \
|
{ IPU, "IN-PLACE" }, \
|
||||||
{ OPU, "OUT-OF-PLACE" })
|
{ OPU, "OUT-OF-PLACE" })
|
||||||
|
|
||||||
@ -727,7 +728,8 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio,
|
|||||||
__field(dev_t, dev)
|
__field(dev_t, dev)
|
||||||
__field(ino_t, ino)
|
__field(ino_t, ino)
|
||||||
__field(pgoff_t, index)
|
__field(pgoff_t, index)
|
||||||
__field(block_t, blkaddr)
|
__field(block_t, old_blkaddr)
|
||||||
|
__field(block_t, new_blkaddr)
|
||||||
__field(int, rw)
|
__field(int, rw)
|
||||||
__field(int, type)
|
__field(int, type)
|
||||||
),
|
),
|
||||||
@ -736,16 +738,18 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio,
|
|||||||
__entry->dev = page->mapping->host->i_sb->s_dev;
|
__entry->dev = page->mapping->host->i_sb->s_dev;
|
||||||
__entry->ino = page->mapping->host->i_ino;
|
__entry->ino = page->mapping->host->i_ino;
|
||||||
__entry->index = page->index;
|
__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->rw = fio->rw;
|
||||||
__entry->type = fio->type;
|
__entry->type = fio->type;
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, "
|
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),
|
show_dev_ino(__entry),
|
||||||
(unsigned long)__entry->index,
|
(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_bio_type(__entry->rw),
|
||||||
show_block_type(__entry->type))
|
show_block_type(__entry->type))
|
||||||
);
|
);
|
||||||
|
@ -246,6 +246,24 @@ struct fsxattr {
|
|||||||
#define FS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr)
|
#define FS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr)
|
||||||
#define FS_IOC_FSSETXATTR _IOW ('X', 32, 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)
|
* Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user