Merge tag 'for-f2fs-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs
Pull f2fs updates from Jaegeuk Kim: "New features: - per-file encryption (e.g., ext4) - FALLOC_FL_ZERO_RANGE - FALLOC_FL_COLLAPSE_RANGE - RENAME_WHITEOUT Major enhancement/fixes: - recovery broken superblocks - enhance f2fs_trim_fs with a discard_map - fix a race condition on dentry block allocation - fix a deadlock during summary operation - fix a missing fiemap result .. and many minor bug fixes and clean-ups were done" * tag 'for-f2fs-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (83 commits) f2fs: do not trim preallocated blocks when truncating after i_size f2fs crypto: add alloc_bounce_page f2fs crypto: fix to handle errors likewise ext4 f2fs: drop the volatile_write flag only f2fs: skip committing valid superblock f2fs: setting discard option in parse_options() f2fs: fix to return exact trimmed size f2fs: support FALLOC_FL_INSERT_RANGE f2fs: hide common code in f2fs_replace_block f2fs: disable the discard option when device doesn't support f2fs crypto: remove alloc_page for bounce_page f2fs: fix a deadlock for summary page lock vs. sentry_lock f2fs crypto: clean up error handling in f2fs_fname_setup_filename f2fs crypto: avoid f2fs_inherit_context for symlink f2fs crypto: do not set encryption policy for non-directory by ioctl f2fs crypto: allow setting encryption policy once f2fs crypto: check context consistent for rename2 f2fs: avoid duplicated code by reusing f2fs_read_end_io f2fs crypto: use per-inode tfm structure f2fs: recovering broken superblock during mount ...
This commit is contained in:
commit
cfcc0ad47f
@ -72,6 +72,25 @@ config F2FS_CHECK_FS
|
||||
|
||||
If you want to improve the performance, say N.
|
||||
|
||||
config F2FS_FS_ENCRYPTION
|
||||
bool "F2FS Encryption"
|
||||
depends on F2FS_FS
|
||||
depends on F2FS_FS_XATTR
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_XTS
|
||||
select CRYPTO_CTS
|
||||
select CRYPTO_CTR
|
||||
select CRYPTO_SHA256
|
||||
select KEYS
|
||||
select ENCRYPTED_KEYS
|
||||
help
|
||||
Enable encryption of f2fs 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.
|
||||
|
||||
config F2FS_IO_TRACE
|
||||
bool "F2FS IO tracer"
|
||||
depends on F2FS_FS
|
||||
|
@ -6,3 +6,5 @@ f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
|
||||
f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
|
||||
f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
|
||||
f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o
|
||||
f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o crypto.o \
|
||||
crypto_key.o crypto_fname.o
|
||||
|
@ -334,51 +334,45 @@ static int f2fs_acl_create(struct inode *dir, umode_t *mode,
|
||||
struct page *dpage)
|
||||
{
|
||||
struct posix_acl *p;
|
||||
struct posix_acl *clone;
|
||||
int ret;
|
||||
|
||||
*acl = NULL;
|
||||
*default_acl = NULL;
|
||||
|
||||
if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
|
||||
goto no_acl;
|
||||
return 0;
|
||||
|
||||
p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
|
||||
if (IS_ERR(p)) {
|
||||
if (p == ERR_PTR(-EOPNOTSUPP))
|
||||
goto apply_umask;
|
||||
return PTR_ERR(p);
|
||||
if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
|
||||
*mode &= ~current_umask();
|
||||
return 0;
|
||||
}
|
||||
if (IS_ERR(p))
|
||||
return PTR_ERR(p);
|
||||
|
||||
if (!p)
|
||||
goto apply_umask;
|
||||
|
||||
*acl = f2fs_acl_clone(p, GFP_NOFS);
|
||||
if (!*acl)
|
||||
clone = f2fs_acl_clone(p, GFP_NOFS);
|
||||
if (!clone)
|
||||
goto no_mem;
|
||||
|
||||
ret = f2fs_acl_create_masq(*acl, mode);
|
||||
ret = f2fs_acl_create_masq(clone, mode);
|
||||
if (ret < 0)
|
||||
goto no_mem_clone;
|
||||
|
||||
if (ret == 0) {
|
||||
posix_acl_release(*acl);
|
||||
*acl = NULL;
|
||||
}
|
||||
if (ret == 0)
|
||||
posix_acl_release(clone);
|
||||
else
|
||||
*acl = clone;
|
||||
|
||||
if (!S_ISDIR(*mode)) {
|
||||
if (!S_ISDIR(*mode))
|
||||
posix_acl_release(p);
|
||||
*default_acl = NULL;
|
||||
} else {
|
||||
else
|
||||
*default_acl = p;
|
||||
}
|
||||
return 0;
|
||||
|
||||
apply_umask:
|
||||
*mode &= ~current_umask();
|
||||
no_acl:
|
||||
*default_acl = NULL;
|
||||
*acl = NULL;
|
||||
return 0;
|
||||
|
||||
no_mem_clone:
|
||||
posix_acl_release(*acl);
|
||||
posix_acl_release(clone);
|
||||
no_mem:
|
||||
posix_acl_release(p);
|
||||
return -ENOMEM;
|
||||
|
@ -52,9 +52,11 @@ struct page *get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index)
|
||||
struct address_space *mapping = META_MAPPING(sbi);
|
||||
struct page *page;
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = sbi,
|
||||
.type = META,
|
||||
.rw = READ_SYNC | REQ_META | REQ_PRIO,
|
||||
.blk_addr = index,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
repeat:
|
||||
page = grab_cache_page(mapping, index);
|
||||
@ -65,7 +67,9 @@ repeat:
|
||||
if (PageUptodate(page))
|
||||
goto out;
|
||||
|
||||
if (f2fs_submit_page_bio(sbi, page, &fio))
|
||||
fio.page = page;
|
||||
|
||||
if (f2fs_submit_page_bio(&fio))
|
||||
goto repeat;
|
||||
|
||||
lock_page(page);
|
||||
@ -77,8 +81,7 @@ out:
|
||||
return page;
|
||||
}
|
||||
|
||||
static inline bool is_valid_blkaddr(struct f2fs_sb_info *sbi,
|
||||
block_t blkaddr, int type)
|
||||
bool is_valid_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type)
|
||||
{
|
||||
switch (type) {
|
||||
case META_NAT:
|
||||
@ -118,8 +121,10 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type
|
||||
struct page *page;
|
||||
block_t blkno = start;
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = sbi,
|
||||
.type = META,
|
||||
.rw = READ_SYNC | REQ_META | REQ_PRIO
|
||||
.rw = READ_SYNC | REQ_META | REQ_PRIO,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
|
||||
for (; nrpages-- > 0; blkno++) {
|
||||
@ -161,7 +166,8 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type
|
||||
continue;
|
||||
}
|
||||
|
||||
f2fs_submit_page_mbio(sbi, page, &fio);
|
||||
fio.page = page;
|
||||
f2fs_submit_page_mbio(&fio);
|
||||
f2fs_put_page(page, 0);
|
||||
}
|
||||
out:
|
||||
@ -510,7 +516,12 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
|
||||
grab_meta_page(sbi, start_blk + index);
|
||||
|
||||
index = 1;
|
||||
spin_lock(&im->ino_lock);
|
||||
|
||||
/*
|
||||
* we don't need to do spin_lock(&im->ino_lock) here, since all the
|
||||
* orphan inode operations are covered under f2fs_lock_op().
|
||||
* And, spin_lock should be avoided due to page operations below.
|
||||
*/
|
||||
head = &im->ino_list;
|
||||
|
||||
/* loop for each orphan inode entry and write them in Jornal block */
|
||||
@ -550,8 +561,6 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
|
||||
set_page_dirty(page);
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
spin_unlock(&im->ino_lock);
|
||||
}
|
||||
|
||||
static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
|
||||
@ -879,10 +888,8 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num;
|
||||
nid_t last_nid = nm_i->next_scan_nid;
|
||||
block_t start_blk;
|
||||
struct page *cp_page;
|
||||
unsigned int data_sum_blocks, orphan_blocks;
|
||||
__u32 crc32 = 0;
|
||||
void *kaddr;
|
||||
int i;
|
||||
int cp_payload_blks = __cp_payload(sbi);
|
||||
|
||||
@ -979,19 +986,11 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
start_blk = __start_cp_addr(sbi);
|
||||
|
||||
/* write out checkpoint buffer at block 0 */
|
||||
cp_page = grab_meta_page(sbi, start_blk++);
|
||||
kaddr = page_address(cp_page);
|
||||
memcpy(kaddr, ckpt, F2FS_BLKSIZE);
|
||||
set_page_dirty(cp_page);
|
||||
f2fs_put_page(cp_page, 1);
|
||||
update_meta_page(sbi, ckpt, start_blk++);
|
||||
|
||||
for (i = 1; i < 1 + cp_payload_blks; i++) {
|
||||
cp_page = grab_meta_page(sbi, start_blk++);
|
||||
kaddr = page_address(cp_page);
|
||||
memcpy(kaddr, (char *)ckpt + i * F2FS_BLKSIZE, F2FS_BLKSIZE);
|
||||
set_page_dirty(cp_page);
|
||||
f2fs_put_page(cp_page, 1);
|
||||
}
|
||||
for (i = 1; i < 1 + cp_payload_blks; i++)
|
||||
update_meta_page(sbi, (char *)ckpt + i * F2FS_BLKSIZE,
|
||||
start_blk++);
|
||||
|
||||
if (orphan_num) {
|
||||
write_orphan_inodes(sbi, start_blk);
|
||||
@ -1006,11 +1005,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
}
|
||||
|
||||
/* writeout checkpoint block */
|
||||
cp_page = grab_meta_page(sbi, start_blk);
|
||||
kaddr = page_address(cp_page);
|
||||
memcpy(kaddr, ckpt, F2FS_BLKSIZE);
|
||||
set_page_dirty(cp_page);
|
||||
f2fs_put_page(cp_page, 1);
|
||||
update_meta_page(sbi, ckpt, start_blk);
|
||||
|
||||
/* wait for previous submitted node/meta pages writeback */
|
||||
wait_on_all_pages_writeback(sbi);
|
||||
@ -1036,7 +1031,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return;
|
||||
|
||||
clear_prefree_segments(sbi);
|
||||
clear_prefree_segments(sbi, cpc);
|
||||
clear_sbi_flag(sbi, SBI_IS_DIRTY);
|
||||
}
|
||||
|
||||
@ -1051,7 +1046,8 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
|
||||
if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) &&
|
||||
(cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC))
|
||||
(cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC ||
|
||||
(cpc->reason == CP_DISCARD && !sbi->discard_blks)))
|
||||
goto out;
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
goto out;
|
||||
|
491
fs/f2fs/crypto.c
Normal file
491
fs/f2fs/crypto.c
Normal file
@ -0,0 +1,491 @@
|
||||
/*
|
||||
* 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/hash.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <linux/crypto.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 ablkcipher_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_ablkcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
|
||||
req = ablkcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_request_alloc() failed\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ablkcipher_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);
|
||||
ablkcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE,
|
||||
xts_tweak);
|
||||
if (rw == F2FS_DECRYPT)
|
||||
res = crypto_ablkcipher_decrypt(req);
|
||||
else
|
||||
res = crypto_ablkcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
BUG_ON(req->base.data != &ecr);
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
ablkcipher_request_free(req);
|
||||
if (res) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_ablkcipher_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;
|
||||
}
|
440
fs/f2fs/crypto_fname.c
Normal file
440
fs/f2fs/crypto_fname.c
Normal file
@ -0,0 +1,440 @@
|
||||
/*
|
||||
* linux/fs/f2fs/crypto_fname.c
|
||||
*
|
||||
* Copied from linux/fs/ext4/crypto.c
|
||||
*
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
* Copyright (C) 2015, Motorola Mobility
|
||||
*
|
||||
* This contains functions for filename crypto management in f2fs
|
||||
*
|
||||
* Written by Uday Savagaonkar, 2014.
|
||||
*
|
||||
* Adjust f2fs dentry structure
|
||||
* Jaegeuk Kim, 2015.
|
||||
*
|
||||
* This has not yet undergone a rigorous security audit.
|
||||
*/
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/spinlock_types.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/ratelimit.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "f2fs_crypto.h"
|
||||
#include "xattr.h"
|
||||
|
||||
/**
|
||||
* f2fs_dir_crypt_complete() -
|
||||
*/
|
||||
static void f2fs_dir_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);
|
||||
}
|
||||
|
||||
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() -
|
||||
*
|
||||
* This function encrypts the input filename, and returns the length of the
|
||||
* ciphertext. Errors are returned as negative numbers. We trust the caller to
|
||||
* allocate sufficient memory to oname string.
|
||||
*/
|
||||
static int f2fs_fname_encrypt(struct inode *inode,
|
||||
const struct qstr *iname, struct f2fs_str *oname)
|
||||
{
|
||||
u32 ciphertext_len;
|
||||
struct ablkcipher_request *req = NULL;
|
||||
DECLARE_F2FS_COMPLETION_RESULT(ecr);
|
||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
||||
struct crypto_ablkcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
char iv[F2FS_CRYPTO_BLOCK_SIZE];
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
int padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK);
|
||||
char *workbuf, buf[32], *alloc_buf = NULL;
|
||||
unsigned lim = max_name_len(inode);
|
||||
|
||||
if (iname->len <= 0 || iname->len > lim)
|
||||
return -EIO;
|
||||
|
||||
ciphertext_len = (iname->len < F2FS_CRYPTO_BLOCK_SIZE) ?
|
||||
F2FS_CRYPTO_BLOCK_SIZE : iname->len;
|
||||
ciphertext_len = f2fs_fname_crypto_round_up(ciphertext_len, padding);
|
||||
ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len;
|
||||
|
||||
if (ciphertext_len <= sizeof(buf)) {
|
||||
workbuf = buf;
|
||||
} else {
|
||||
alloc_buf = kmalloc(ciphertext_len, GFP_NOFS);
|
||||
if (!alloc_buf)
|
||||
return -ENOMEM;
|
||||
workbuf = alloc_buf;
|
||||
}
|
||||
|
||||
/* Allocate request */
|
||||
req = ablkcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_request_alloc() failed\n", __func__);
|
||||
kfree(alloc_buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ablkcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
f2fs_dir_crypt_complete, &ecr);
|
||||
|
||||
/* Copy the input */
|
||||
memcpy(workbuf, iname->name, iname->len);
|
||||
if (iname->len < ciphertext_len)
|
||||
memset(workbuf + iname->len, 0, ciphertext_len - iname->len);
|
||||
|
||||
/* Initialize IV */
|
||||
memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE);
|
||||
|
||||
/* Create encryption request */
|
||||
sg_init_one(&src_sg, workbuf, ciphertext_len);
|
||||
sg_init_one(&dst_sg, oname->name, ciphertext_len);
|
||||
ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv);
|
||||
res = crypto_ablkcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
BUG_ON(req->base.data != &ecr);
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
kfree(alloc_buf);
|
||||
ablkcipher_request_free(req);
|
||||
if (res < 0) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: Error (error code %d)\n", __func__, res);
|
||||
}
|
||||
oname->len = ciphertext_len;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* f2fs_fname_decrypt()
|
||||
* This function decrypts the input filename, and returns
|
||||
* the length of the plaintext.
|
||||
* Errors are returned as negative numbers.
|
||||
* We trust the caller to allocate sufficient memory to oname string.
|
||||
*/
|
||||
static int f2fs_fname_decrypt(struct inode *inode,
|
||||
const struct f2fs_str *iname, struct f2fs_str *oname)
|
||||
{
|
||||
struct ablkcipher_request *req = NULL;
|
||||
DECLARE_F2FS_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
||||
struct crypto_ablkcipher *tfm = ci->ci_ctfm;
|
||||
int res = 0;
|
||||
char iv[F2FS_CRYPTO_BLOCK_SIZE];
|
||||
unsigned lim = max_name_len(inode);
|
||||
|
||||
if (iname->len <= 0 || iname->len > lim)
|
||||
return -EIO;
|
||||
|
||||
/* Allocate request */
|
||||
req = ablkcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: crypto_request_alloc() failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ablkcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
f2fs_dir_crypt_complete, &ecr);
|
||||
|
||||
/* Initialize IV */
|
||||
memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE);
|
||||
|
||||
/* Create decryption request */
|
||||
sg_init_one(&src_sg, iname->name, iname->len);
|
||||
sg_init_one(&dst_sg, oname->name, oname->len);
|
||||
ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv);
|
||||
res = crypto_ablkcipher_decrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
BUG_ON(req->base.data != &ecr);
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
ablkcipher_request_free(req);
|
||||
if (res < 0) {
|
||||
printk_ratelimited(KERN_ERR
|
||||
"%s: Error in f2fs_fname_decrypt (error code %d)\n",
|
||||
__func__, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
oname->len = strnlen(oname->name, iname->len);
|
||||
return oname->len;
|
||||
}
|
||||
|
||||
static const char *lookup_table =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
||||
|
||||
/**
|
||||
* f2fs_fname_encode_digest() -
|
||||
*
|
||||
* Encodes the input digest using characters from the set [a-zA-Z0-9_+].
|
||||
* The encoded string is roughly 4/3 times the size of the input string.
|
||||
*/
|
||||
static int digest_encode(const char *src, int len, char *dst)
|
||||
{
|
||||
int i = 0, bits = 0, ac = 0;
|
||||
char *cp = dst;
|
||||
|
||||
while (i < len) {
|
||||
ac += (((unsigned char) src[i]) << bits);
|
||||
bits += 8;
|
||||
do {
|
||||
*cp++ = lookup_table[ac & 0x3f];
|
||||
ac >>= 6;
|
||||
bits -= 6;
|
||||
} while (bits >= 6);
|
||||
i++;
|
||||
}
|
||||
if (bits)
|
||||
*cp++ = lookup_table[ac & 0x3f];
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
static int digest_decode(const char *src, int len, char *dst)
|
||||
{
|
||||
int i = 0, bits = 0, ac = 0;
|
||||
const char *p;
|
||||
char *cp = dst;
|
||||
|
||||
while (i < len) {
|
||||
p = strchr(lookup_table, src[i]);
|
||||
if (p == NULL || src[i] == 0)
|
||||
return -2;
|
||||
ac += (p - lookup_table) << bits;
|
||||
bits += 6;
|
||||
if (bits >= 8) {
|
||||
*cp++ = ac & 0xff;
|
||||
ac >>= 8;
|
||||
bits -= 8;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (ac)
|
||||
return -1;
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_fname_crypto_alloc_obuff() -
|
||||
*
|
||||
* Allocates an output buffer that is sufficient for the crypto operation
|
||||
* specified by the context and the direction.
|
||||
*/
|
||||
int f2fs_fname_crypto_alloc_buffer(struct inode *inode,
|
||||
u32 ilen, struct f2fs_str *crypto_str)
|
||||
{
|
||||
unsigned int olen;
|
||||
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;
|
||||
if (olen < F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
|
||||
olen = F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
|
||||
/* Allocated buffer can hold one more character to null-terminate the
|
||||
* string */
|
||||
crypto_str->name = kmalloc(olen + 1, GFP_NOFS);
|
||||
if (!(crypto_str->name))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_fname_crypto_free_buffer() -
|
||||
*
|
||||
* Frees the buffer allocated for crypto operation.
|
||||
*/
|
||||
void f2fs_fname_crypto_free_buffer(struct f2fs_str *crypto_str)
|
||||
{
|
||||
if (!crypto_str)
|
||||
return;
|
||||
kfree(crypto_str->name);
|
||||
crypto_str->name = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_fname_disk_to_usr() - converts a filename from disk space to user space
|
||||
*/
|
||||
int f2fs_fname_disk_to_usr(struct inode *inode,
|
||||
f2fs_hash_t *hash,
|
||||
const struct f2fs_str *iname,
|
||||
struct f2fs_str *oname)
|
||||
{
|
||||
const struct qstr qname = FSTR_TO_QSTR(iname);
|
||||
char buf[24];
|
||||
int ret;
|
||||
|
||||
if (is_dot_dotdot(&qname)) {
|
||||
oname->name[0] = '.';
|
||||
oname->name[iname->len - 1] = '.';
|
||||
oname->len = iname->len;
|
||||
return oname->len;
|
||||
}
|
||||
|
||||
if (F2FS_I(inode)->i_crypt_info)
|
||||
return f2fs_fname_decrypt(inode, iname, oname);
|
||||
|
||||
if (iname->len <= F2FS_FNAME_CRYPTO_DIGEST_SIZE) {
|
||||
ret = digest_encode(iname->name, iname->len, oname->name);
|
||||
oname->len = ret;
|
||||
return ret;
|
||||
}
|
||||
if (hash) {
|
||||
memcpy(buf, hash, 4);
|
||||
memset(buf + 4, 0, 4);
|
||||
} else
|
||||
memset(buf, 0, 8);
|
||||
memcpy(buf + 8, iname->name + iname->len - 16, 16);
|
||||
oname->name[0] = '_';
|
||||
ret = digest_encode(buf, 24, oname->name + 1);
|
||||
oname->len = ret + 1;
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* f2fs_fname_usr_to_disk() - converts a filename from user space to disk space
|
||||
*/
|
||||
int f2fs_fname_usr_to_disk(struct inode *inode,
|
||||
const struct qstr *iname,
|
||||
struct f2fs_str *oname)
|
||||
{
|
||||
int res;
|
||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
||||
|
||||
if (is_dot_dotdot(iname)) {
|
||||
oname->name[0] = '.';
|
||||
oname->name[iname->len - 1] = '.';
|
||||
oname->len = iname->len;
|
||||
return oname->len;
|
||||
}
|
||||
|
||||
if (ci) {
|
||||
res = f2fs_fname_encrypt(inode, iname, oname);
|
||||
return res;
|
||||
}
|
||||
/* Without a proper key, a user is not allowed to modify the filenames
|
||||
* in a directory. Consequently, a user space name cannot be mapped to
|
||||
* a disk-space name */
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname,
|
||||
int lookup, struct f2fs_filename *fname)
|
||||
{
|
||||
struct f2fs_crypt_info *ci;
|
||||
int ret = 0, bigname = 0;
|
||||
|
||||
memset(fname, 0, sizeof(struct f2fs_filename));
|
||||
fname->usr_fname = iname;
|
||||
|
||||
if (!f2fs_encrypted_inode(dir) || is_dot_dotdot(iname)) {
|
||||
fname->disk_name.name = (unsigned char *)iname->name;
|
||||
fname->disk_name.len = iname->len;
|
||||
return 0;
|
||||
}
|
||||
ret = f2fs_get_encryption_info(dir);
|
||||
if (ret)
|
||||
return ret;
|
||||
ci = F2FS_I(dir)->i_crypt_info;
|
||||
if (ci) {
|
||||
ret = f2fs_fname_crypto_alloc_buffer(dir, iname->len,
|
||||
&fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = f2fs_fname_encrypt(dir, iname, &fname->crypto_buf);
|
||||
if (ret < 0)
|
||||
goto errout;
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
return 0;
|
||||
}
|
||||
if (!lookup)
|
||||
return -EACCES;
|
||||
|
||||
/* We don't have the key and we are doing a lookup; decode the
|
||||
* user-supplied name
|
||||
*/
|
||||
if (iname->name[0] == '_')
|
||||
bigname = 1;
|
||||
if ((bigname && (iname->len != 33)) ||
|
||||
(!bigname && (iname->len > 43)))
|
||||
return -ENOENT;
|
||||
|
||||
fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
|
||||
if (fname->crypto_buf.name == NULL)
|
||||
return -ENOMEM;
|
||||
ret = digest_decode(iname->name + bigname, iname->len - bigname,
|
||||
fname->crypto_buf.name);
|
||||
if (ret < 0) {
|
||||
ret = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
fname->crypto_buf.len = ret;
|
||||
if (bigname) {
|
||||
memcpy(&fname->hash, fname->crypto_buf.name, 4);
|
||||
} else {
|
||||
fname->disk_name.name = fname->crypto_buf.name;
|
||||
fname->disk_name.len = fname->crypto_buf.len;
|
||||
}
|
||||
return 0;
|
||||
errout:
|
||||
f2fs_fname_crypto_free_buffer(&fname->crypto_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void f2fs_fname_free_filename(struct f2fs_filename *fname)
|
||||
{
|
||||
kfree(fname->crypto_buf.name);
|
||||
fname->crypto_buf.name = NULL;
|
||||
fname->usr_fname = NULL;
|
||||
fname->disk_name.name = NULL;
|
||||
}
|
255
fs/f2fs/crypto_key.c
Normal file
255
fs/f2fs/crypto_key.c
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* 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/hash.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 ablkcipher_request *req = NULL;
|
||||
DECLARE_F2FS_COMPLETION_RESULT(ecr);
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0,
|
||||
0);
|
||||
|
||||
if (IS_ERR(tfm)) {
|
||||
res = PTR_ERR(tfm);
|
||||
tfm = NULL;
|
||||
goto out;
|
||||
}
|
||||
crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
|
||||
req = ablkcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
ablkcipher_request_set_callback(req,
|
||||
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
derive_crypt_complete, &ecr);
|
||||
res = crypto_ablkcipher_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);
|
||||
ablkcipher_request_set_crypt(req, &src_sg, &dst_sg,
|
||||
F2FS_AES_256_XTS_KEY_SIZE, NULL);
|
||||
res = crypto_ablkcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
BUG_ON(req->base.data != &ecr);
|
||||
wait_for_completion(&ecr.completion);
|
||||
res = ecr.res;
|
||||
}
|
||||
out:
|
||||
if (req)
|
||||
ablkcipher_request_free(req);
|
||||
if (tfm)
|
||||
crypto_free_ablkcipher(tfm);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void f2fs_free_crypt_info(struct f2fs_crypt_info *ci)
|
||||
{
|
||||
if (!ci)
|
||||
return;
|
||||
|
||||
if (ci->ci_keyring_key)
|
||||
key_put(ci->ci_keyring_key);
|
||||
crypto_free_ablkcipher(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;
|
||||
struct user_key_payload *ukp;
|
||||
struct crypto_ablkcipher *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 = ((struct user_key_payload *)keyring_key->payload.data);
|
||||
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_ablkcipher(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_ablkcipher_clear_flags(ctfm, ~0);
|
||||
crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm),
|
||||
CRYPTO_TFM_REQ_WEAK_KEY);
|
||||
res = crypto_ablkcipher_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);
|
||||
}
|
209
fs/f2fs/crypto_policy.c
Normal file
209
fs/f2fs/crypto_policy.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
593
fs/f2fs/data.c
593
fs/f2fs/data.c
@ -18,6 +18,7 @@
|
||||
#include <linux/bio.h>
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/cleancache.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@ -33,6 +34,15 @@ static void f2fs_read_end_io(struct bio *bio, int err)
|
||||
struct bio_vec *bvec;
|
||||
int i;
|
||||
|
||||
if (f2fs_bio_encrypted(bio)) {
|
||||
if (err) {
|
||||
f2fs_release_crypto_ctx(bio->bi_private);
|
||||
} else {
|
||||
f2fs_end_io_crypto_work(bio->bi_private, bio);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bio_for_each_segment_all(bvec, bio, i) {
|
||||
struct page *page = bvec->bv_page;
|
||||
|
||||
@ -56,6 +66,8 @@ static void f2fs_write_end_io(struct bio *bio, int err)
|
||||
bio_for_each_segment_all(bvec, bio, i) {
|
||||
struct page *page = bvec->bv_page;
|
||||
|
||||
f2fs_restore_and_release_control_page(&page);
|
||||
|
||||
if (unlikely(err)) {
|
||||
set_page_dirty(page);
|
||||
set_bit(AS_EIO, &page->mapping->flags);
|
||||
@ -86,7 +98,7 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr,
|
||||
bio->bi_bdev = sbi->sb->s_bdev;
|
||||
bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(blk_addr);
|
||||
bio->bi_end_io = is_read ? f2fs_read_end_io : f2fs_write_end_io;
|
||||
bio->bi_private = sbi;
|
||||
bio->bi_private = is_read ? NULL : sbi;
|
||||
|
||||
return bio;
|
||||
}
|
||||
@ -133,16 +145,16 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
|
||||
* Fill the locked page with data located in the block address.
|
||||
* Return unlocked page.
|
||||
*/
|
||||
int f2fs_submit_page_bio(struct f2fs_sb_info *sbi, struct page *page,
|
||||
struct f2fs_io_info *fio)
|
||||
int f2fs_submit_page_bio(struct f2fs_io_info *fio)
|
||||
{
|
||||
struct bio *bio;
|
||||
struct page *page = fio->encrypted_page ? fio->encrypted_page : fio->page;
|
||||
|
||||
trace_f2fs_submit_page_bio(page, fio);
|
||||
f2fs_trace_ios(page, fio, 0);
|
||||
f2fs_trace_ios(fio, 0);
|
||||
|
||||
/* Allocate a new bio */
|
||||
bio = __bio_alloc(sbi, fio->blk_addr, 1, is_read_io(fio->rw));
|
||||
bio = __bio_alloc(fio->sbi, fio->blk_addr, 1, is_read_io(fio->rw));
|
||||
|
||||
if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) {
|
||||
bio_put(bio);
|
||||
@ -154,12 +166,13 @@ int f2fs_submit_page_bio(struct f2fs_sb_info *sbi, struct page *page,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f2fs_submit_page_mbio(struct f2fs_sb_info *sbi, struct page *page,
|
||||
struct f2fs_io_info *fio)
|
||||
void f2fs_submit_page_mbio(struct f2fs_io_info *fio)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = fio->sbi;
|
||||
enum page_type btype = PAGE_TYPE_OF_BIO(fio->type);
|
||||
struct f2fs_bio_info *io;
|
||||
bool is_read = is_read_io(fio->rw);
|
||||
struct page *bio_page;
|
||||
|
||||
io = is_read ? &sbi->read_io : &sbi->write_io[btype];
|
||||
|
||||
@ -181,17 +194,19 @@ alloc_new:
|
||||
io->fio = *fio;
|
||||
}
|
||||
|
||||
if (bio_add_page(io->bio, page, PAGE_CACHE_SIZE, 0) <
|
||||
bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page;
|
||||
|
||||
if (bio_add_page(io->bio, bio_page, PAGE_CACHE_SIZE, 0) <
|
||||
PAGE_CACHE_SIZE) {
|
||||
__submit_merged_bio(io);
|
||||
goto alloc_new;
|
||||
}
|
||||
|
||||
io->last_block_in_bio = fio->blk_addr;
|
||||
f2fs_trace_ios(page, fio, 0);
|
||||
f2fs_trace_ios(fio, 0);
|
||||
|
||||
up_write(&io->io_rwsem);
|
||||
trace_f2fs_submit_page_mbio(page, fio);
|
||||
trace_f2fs_submit_page_mbio(fio->page, fio);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -251,19 +266,6 @@ int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void f2fs_map_bh(struct super_block *sb, pgoff_t pgofs,
|
||||
struct extent_info *ei, struct buffer_head *bh_result)
|
||||
{
|
||||
unsigned int blkbits = sb->s_blocksize_bits;
|
||||
size_t max_size = bh_result->b_size;
|
||||
size_t mapped_size;
|
||||
|
||||
clear_buffer_new(bh_result);
|
||||
map_bh(bh_result, sb, ei->blk + pgofs - ei->fofs);
|
||||
mapped_size = (ei->fofs + ei->len - pgofs) << blkbits;
|
||||
bh_result->b_size = min(max_size, mapped_size);
|
||||
}
|
||||
|
||||
static bool lookup_extent_info(struct inode *inode, pgoff_t pgofs,
|
||||
struct extent_info *ei)
|
||||
{
|
||||
@ -905,7 +907,7 @@ void f2fs_update_extent_cache(struct dnode_of_data *dn)
|
||||
sync_inode_page(dn);
|
||||
}
|
||||
|
||||
struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync)
|
||||
struct page *get_read_data_page(struct inode *inode, pgoff_t index, int rw)
|
||||
{
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct dnode_of_data dn;
|
||||
@ -913,83 +915,15 @@ struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync)
|
||||
struct extent_info ei;
|
||||
int err;
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(inode),
|
||||
.type = DATA,
|
||||
.rw = sync ? READ_SYNC : READA,
|
||||
.rw = rw,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* If sync is false, it needs to check its block allocation.
|
||||
* This is need and triggered by two flows:
|
||||
* gc and truncate_partial_data_page.
|
||||
*/
|
||||
if (!sync)
|
||||
goto search;
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
return read_mapping_page(mapping, index, NULL);
|
||||
|
||||
page = find_get_page(mapping, index);
|
||||
if (page && PageUptodate(page))
|
||||
return page;
|
||||
f2fs_put_page(page, 0);
|
||||
search:
|
||||
if (f2fs_lookup_extent_cache(inode, index, &ei)) {
|
||||
dn.data_blkaddr = ei.blk + index - ei.fofs;
|
||||
goto got_it;
|
||||
}
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
f2fs_put_dnode(&dn);
|
||||
|
||||
if (dn.data_blkaddr == NULL_ADDR)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
/* By fallocate(), there is no cached page, but with NEW_ADDR */
|
||||
if (unlikely(dn.data_blkaddr == NEW_ADDR))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
got_it:
|
||||
page = grab_cache_page(mapping, index);
|
||||
if (!page)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (PageUptodate(page)) {
|
||||
unlock_page(page);
|
||||
return page;
|
||||
}
|
||||
|
||||
fio.blk_addr = dn.data_blkaddr;
|
||||
err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, &fio);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
if (sync) {
|
||||
wait_on_page_locked(page);
|
||||
if (unlikely(!PageUptodate(page))) {
|
||||
f2fs_put_page(page, 0);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it tries to access a hole, return an error.
|
||||
* Because, the callers, functions in dir.c and GC, should be able to know
|
||||
* whether this page exists or not.
|
||||
*/
|
||||
struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
|
||||
{
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct dnode_of_data dn;
|
||||
struct page *page;
|
||||
struct extent_info ei;
|
||||
int err;
|
||||
struct f2fs_io_info fio = {
|
||||
.type = DATA,
|
||||
.rw = READ_SYNC,
|
||||
};
|
||||
repeat:
|
||||
page = grab_cache_page(mapping, index);
|
||||
if (!page)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -1011,10 +945,11 @@ repeat:
|
||||
f2fs_put_page(page, 1);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
got_it:
|
||||
if (PageUptodate(page))
|
||||
if (PageUptodate(page)) {
|
||||
unlock_page(page);
|
||||
return page;
|
||||
}
|
||||
|
||||
/*
|
||||
* A new dentry page is allocated but not able to be written, since its
|
||||
@ -1025,14 +960,58 @@ got_it:
|
||||
if (dn.data_blkaddr == NEW_ADDR) {
|
||||
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
return page;
|
||||
}
|
||||
|
||||
fio.blk_addr = dn.data_blkaddr;
|
||||
err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, &fio);
|
||||
fio.page = page;
|
||||
err = f2fs_submit_page_bio(&fio);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return page;
|
||||
}
|
||||
|
||||
struct page *find_data_page(struct inode *inode, pgoff_t index)
|
||||
{
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct page *page;
|
||||
|
||||
page = find_get_page(mapping, index);
|
||||
if (page && PageUptodate(page))
|
||||
return page;
|
||||
f2fs_put_page(page, 0);
|
||||
|
||||
page = get_read_data_page(inode, index, READ_SYNC);
|
||||
if (IS_ERR(page))
|
||||
return page;
|
||||
|
||||
if (PageUptodate(page))
|
||||
return page;
|
||||
|
||||
wait_on_page_locked(page);
|
||||
if (unlikely(!PageUptodate(page))) {
|
||||
f2fs_put_page(page, 0);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it tries to access a hole, return an error.
|
||||
* Because, the callers, functions in dir.c and GC, should be able to know
|
||||
* whether this page exists or not.
|
||||
*/
|
||||
struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
|
||||
{
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct page *page;
|
||||
repeat:
|
||||
page = get_read_data_page(inode, index, READ_SYNC);
|
||||
if (IS_ERR(page))
|
||||
return page;
|
||||
|
||||
/* wait for read completion */
|
||||
lock_page(page);
|
||||
if (unlikely(!PageUptodate(page))) {
|
||||
f2fs_put_page(page, 1);
|
||||
@ -1060,46 +1039,37 @@ struct page *get_new_data_page(struct inode *inode,
|
||||
struct page *page;
|
||||
struct dnode_of_data dn;
|
||||
int err;
|
||||
repeat:
|
||||
page = grab_cache_page(mapping, index);
|
||||
if (!page)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
set_new_dnode(&dn, inode, ipage, NULL, 0);
|
||||
err = f2fs_reserve_block(&dn, index);
|
||||
if (err)
|
||||
if (err) {
|
||||
f2fs_put_page(page, 1);
|
||||
return ERR_PTR(err);
|
||||
repeat:
|
||||
page = grab_cache_page(mapping, index);
|
||||
if (!page) {
|
||||
err = -ENOMEM;
|
||||
goto put_err;
|
||||
}
|
||||
if (!ipage)
|
||||
f2fs_put_dnode(&dn);
|
||||
|
||||
if (PageUptodate(page))
|
||||
return page;
|
||||
goto got_it;
|
||||
|
||||
if (dn.data_blkaddr == NEW_ADDR) {
|
||||
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
|
||||
SetPageUptodate(page);
|
||||
} else {
|
||||
struct f2fs_io_info fio = {
|
||||
.type = DATA,
|
||||
.rw = READ_SYNC,
|
||||
.blk_addr = dn.data_blkaddr,
|
||||
};
|
||||
err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, &fio);
|
||||
if (err)
|
||||
goto put_err;
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
lock_page(page);
|
||||
if (unlikely(!PageUptodate(page))) {
|
||||
f2fs_put_page(page, 1);
|
||||
err = -EIO;
|
||||
goto put_err;
|
||||
}
|
||||
if (unlikely(page->mapping != mapping)) {
|
||||
f2fs_put_page(page, 1);
|
||||
page = get_read_data_page(inode, index, READ_SYNC);
|
||||
if (IS_ERR(page))
|
||||
goto repeat;
|
||||
}
|
||||
}
|
||||
|
||||
/* wait for read completion */
|
||||
lock_page(page);
|
||||
}
|
||||
got_it:
|
||||
if (new_i_size &&
|
||||
i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) {
|
||||
i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT));
|
||||
@ -1107,10 +1077,6 @@ repeat:
|
||||
set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR);
|
||||
}
|
||||
return page;
|
||||
|
||||
put_err:
|
||||
f2fs_put_dnode(&dn);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int __allocate_data_block(struct dnode_of_data *dn)
|
||||
@ -1208,18 +1174,18 @@ out:
|
||||
}
|
||||
|
||||
/*
|
||||
* get_data_block() now supported readahead/bmap/rw direct_IO with mapped bh.
|
||||
* f2fs_map_blocks() now supported readahead/bmap/rw direct_IO with
|
||||
* f2fs_map_blocks structure.
|
||||
* If original data blocks are allocated, then give them to blockdev.
|
||||
* Otherwise,
|
||||
* a. preallocate requested block addresses
|
||||
* b. do not use extent cache for better performance
|
||||
* c. give the block addresses to blockdev
|
||||
*/
|
||||
static int __get_data_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh_result, int create, bool fiemap)
|
||||
static int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
|
||||
int create, bool fiemap)
|
||||
{
|
||||
unsigned int blkbits = inode->i_sb->s_blocksize_bits;
|
||||
unsigned maxblocks = bh_result->b_size >> blkbits;
|
||||
unsigned int maxblocks = map->m_len;
|
||||
struct dnode_of_data dn;
|
||||
int mode = create ? ALLOC_NODE : LOOKUP_NODE_RA;
|
||||
pgoff_t pgofs, end_offset;
|
||||
@ -1227,11 +1193,16 @@ static int __get_data_block(struct inode *inode, sector_t iblock,
|
||||
struct extent_info ei;
|
||||
bool allocated = false;
|
||||
|
||||
/* Get the page offset from the block offset(iblock) */
|
||||
pgofs = (pgoff_t)(iblock >> (PAGE_CACHE_SHIFT - blkbits));
|
||||
map->m_len = 0;
|
||||
map->m_flags = 0;
|
||||
|
||||
/* it only supports block size == page size */
|
||||
pgofs = (pgoff_t)map->m_lblk;
|
||||
|
||||
if (f2fs_lookup_extent_cache(inode, pgofs, &ei)) {
|
||||
f2fs_map_bh(inode->i_sb, pgofs, &ei, bh_result);
|
||||
map->m_pblk = ei.blk + pgofs - ei.fofs;
|
||||
map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs);
|
||||
map->m_flags = F2FS_MAP_MAPPED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1250,21 +1221,23 @@ static int __get_data_block(struct inode *inode, sector_t iblock,
|
||||
goto put_out;
|
||||
|
||||
if (dn.data_blkaddr != NULL_ADDR) {
|
||||
clear_buffer_new(bh_result);
|
||||
map_bh(bh_result, inode->i_sb, dn.data_blkaddr);
|
||||
map->m_flags = F2FS_MAP_MAPPED;
|
||||
map->m_pblk = dn.data_blkaddr;
|
||||
if (dn.data_blkaddr == NEW_ADDR)
|
||||
map->m_flags |= F2FS_MAP_UNWRITTEN;
|
||||
} else if (create) {
|
||||
err = __allocate_data_block(&dn);
|
||||
if (err)
|
||||
goto put_out;
|
||||
allocated = true;
|
||||
set_buffer_new(bh_result);
|
||||
map_bh(bh_result, inode->i_sb, dn.data_blkaddr);
|
||||
map->m_flags = F2FS_MAP_NEW | F2FS_MAP_MAPPED;
|
||||
map->m_pblk = dn.data_blkaddr;
|
||||
} else {
|
||||
goto put_out;
|
||||
}
|
||||
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
|
||||
bh_result->b_size = (((size_t)1) << blkbits);
|
||||
map->m_len = 1;
|
||||
dn.ofs_in_node++;
|
||||
pgofs++;
|
||||
|
||||
@ -1288,22 +1261,25 @@ get_next:
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
|
||||
}
|
||||
|
||||
if (maxblocks > (bh_result->b_size >> blkbits)) {
|
||||
if (maxblocks > map->m_len) {
|
||||
block_t blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
|
||||
if (blkaddr == NULL_ADDR && create) {
|
||||
err = __allocate_data_block(&dn);
|
||||
if (err)
|
||||
goto sync_out;
|
||||
allocated = true;
|
||||
set_buffer_new(bh_result);
|
||||
map->m_flags |= F2FS_MAP_NEW;
|
||||
blkaddr = dn.data_blkaddr;
|
||||
}
|
||||
/* Give more consecutive addresses for the readahead */
|
||||
if (blkaddr == (bh_result->b_blocknr + ofs)) {
|
||||
if ((map->m_pblk != NEW_ADDR &&
|
||||
blkaddr == (map->m_pblk + ofs)) ||
|
||||
(map->m_pblk == NEW_ADDR &&
|
||||
blkaddr == NEW_ADDR)) {
|
||||
ofs++;
|
||||
dn.ofs_in_node++;
|
||||
pgofs++;
|
||||
bh_result->b_size += (((size_t)1) << blkbits);
|
||||
map->m_len++;
|
||||
goto get_next;
|
||||
}
|
||||
}
|
||||
@ -1316,10 +1292,28 @@ unlock_out:
|
||||
if (create)
|
||||
f2fs_unlock_op(F2FS_I_SB(inode));
|
||||
out:
|
||||
trace_f2fs_get_data_block(inode, iblock, bh_result, err);
|
||||
trace_f2fs_map_blocks(inode, map, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __get_data_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh, int create, bool fiemap)
|
||||
{
|
||||
struct f2fs_map_blocks map;
|
||||
int ret;
|
||||
|
||||
map.m_lblk = iblock;
|
||||
map.m_len = bh->b_size >> inode->i_blkbits;
|
||||
|
||||
ret = f2fs_map_blocks(inode, &map, create, fiemap);
|
||||
if (!ret) {
|
||||
map_bh(bh, inode->i_sb, map.m_pblk);
|
||||
bh->b_state = (bh->b_state & ~F2FS_MAP_FLAGS) | map.m_flags;
|
||||
bh->b_size = map.m_len << inode->i_blkbits;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_data_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh_result, int create)
|
||||
{
|
||||
@ -1332,11 +1326,268 @@ static int get_data_block_fiemap(struct inode *inode, sector_t iblock,
|
||||
return __get_data_block(inode, iblock, bh_result, create, true);
|
||||
}
|
||||
|
||||
static inline sector_t logical_to_blk(struct inode *inode, loff_t offset)
|
||||
{
|
||||
return (offset >> inode->i_blkbits);
|
||||
}
|
||||
|
||||
static inline loff_t blk_to_logical(struct inode *inode, sector_t blk)
|
||||
{
|
||||
return (blk << inode->i_blkbits);
|
||||
}
|
||||
|
||||
int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
u64 start, u64 len)
|
||||
{
|
||||
return generic_block_fiemap(inode, fieinfo,
|
||||
start, len, get_data_block_fiemap);
|
||||
struct buffer_head map_bh;
|
||||
sector_t start_blk, last_blk;
|
||||
loff_t isize = i_size_read(inode);
|
||||
u64 logical = 0, phys = 0, size = 0;
|
||||
u32 flags = 0;
|
||||
bool past_eof = false, whole_file = false;
|
||||
int ret = 0;
|
||||
|
||||
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
if (len >= isize) {
|
||||
whole_file = true;
|
||||
len = isize;
|
||||
}
|
||||
|
||||
if (logical_to_blk(inode, len) == 0)
|
||||
len = blk_to_logical(inode, 1);
|
||||
|
||||
start_blk = logical_to_blk(inode, start);
|
||||
last_blk = logical_to_blk(inode, start + len - 1);
|
||||
next:
|
||||
memset(&map_bh, 0, sizeof(struct buffer_head));
|
||||
map_bh.b_size = len;
|
||||
|
||||
ret = get_data_block_fiemap(inode, start_blk, &map_bh, 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* HOLE */
|
||||
if (!buffer_mapped(&map_bh)) {
|
||||
start_blk++;
|
||||
|
||||
if (!past_eof && blk_to_logical(inode, start_blk) >= isize)
|
||||
past_eof = 1;
|
||||
|
||||
if (past_eof && size) {
|
||||
flags |= FIEMAP_EXTENT_LAST;
|
||||
ret = fiemap_fill_next_extent(fieinfo, logical,
|
||||
phys, size, flags);
|
||||
} else if (size) {
|
||||
ret = fiemap_fill_next_extent(fieinfo, logical,
|
||||
phys, size, flags);
|
||||
size = 0;
|
||||
}
|
||||
|
||||
/* if we have holes up to/past EOF then we're done */
|
||||
if (start_blk > last_blk || past_eof || ret)
|
||||
goto out;
|
||||
} else {
|
||||
if (start_blk > last_blk && !whole_file) {
|
||||
ret = fiemap_fill_next_extent(fieinfo, logical,
|
||||
phys, size, flags);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* if size != 0 then we know we already have an extent
|
||||
* to add, so add it.
|
||||
*/
|
||||
if (size) {
|
||||
ret = fiemap_fill_next_extent(fieinfo, logical,
|
||||
phys, size, flags);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
logical = blk_to_logical(inode, start_blk);
|
||||
phys = blk_to_logical(inode, map_bh.b_blocknr);
|
||||
size = map_bh.b_size;
|
||||
flags = 0;
|
||||
if (buffer_unwritten(&map_bh))
|
||||
flags = FIEMAP_EXTENT_UNWRITTEN;
|
||||
|
||||
start_blk += logical_to_blk(inode, size);
|
||||
|
||||
/*
|
||||
* If we are past the EOF, then we need to make sure as
|
||||
* soon as we find a hole that the last extent we found
|
||||
* is marked with FIEMAP_EXTENT_LAST
|
||||
*/
|
||||
if (!past_eof && logical + size >= isize)
|
||||
past_eof = true;
|
||||
}
|
||||
cond_resched();
|
||||
if (fatal_signal_pending(current))
|
||||
ret = -EINTR;
|
||||
else
|
||||
goto next;
|
||||
out:
|
||||
if (ret == 1)
|
||||
ret = 0;
|
||||
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function was originally taken from fs/mpage.c, and customized for f2fs.
|
||||
* Major change was from block_size == page_size in f2fs by default.
|
||||
*/
|
||||
static int f2fs_mpage_readpages(struct address_space *mapping,
|
||||
struct list_head *pages, struct page *page,
|
||||
unsigned nr_pages)
|
||||
{
|
||||
struct bio *bio = NULL;
|
||||
unsigned page_idx;
|
||||
sector_t last_block_in_bio = 0;
|
||||
struct inode *inode = mapping->host;
|
||||
const unsigned blkbits = inode->i_blkbits;
|
||||
const unsigned blocksize = 1 << blkbits;
|
||||
sector_t block_in_file;
|
||||
sector_t last_block;
|
||||
sector_t last_block_in_file;
|
||||
sector_t block_nr;
|
||||
struct block_device *bdev = inode->i_sb->s_bdev;
|
||||
struct f2fs_map_blocks map;
|
||||
|
||||
map.m_pblk = 0;
|
||||
map.m_lblk = 0;
|
||||
map.m_len = 0;
|
||||
map.m_flags = 0;
|
||||
|
||||
for (page_idx = 0; nr_pages; page_idx++, nr_pages--) {
|
||||
|
||||
prefetchw(&page->flags);
|
||||
if (pages) {
|
||||
page = list_entry(pages->prev, struct page, lru);
|
||||
list_del(&page->lru);
|
||||
if (add_to_page_cache_lru(page, mapping,
|
||||
page->index, GFP_KERNEL))
|
||||
goto next_page;
|
||||
}
|
||||
|
||||
block_in_file = (sector_t)page->index;
|
||||
last_block = block_in_file + nr_pages;
|
||||
last_block_in_file = (i_size_read(inode) + blocksize - 1) >>
|
||||
blkbits;
|
||||
if (last_block > last_block_in_file)
|
||||
last_block = last_block_in_file;
|
||||
|
||||
/*
|
||||
* Map blocks using the previous result first.
|
||||
*/
|
||||
if ((map.m_flags & F2FS_MAP_MAPPED) &&
|
||||
block_in_file > map.m_lblk &&
|
||||
block_in_file < (map.m_lblk + map.m_len))
|
||||
goto got_it;
|
||||
|
||||
/*
|
||||
* Then do more f2fs_map_blocks() calls until we are
|
||||
* done with this page.
|
||||
*/
|
||||
map.m_flags = 0;
|
||||
|
||||
if (block_in_file < last_block) {
|
||||
map.m_lblk = block_in_file;
|
||||
map.m_len = last_block - block_in_file;
|
||||
|
||||
if (f2fs_map_blocks(inode, &map, 0, false))
|
||||
goto set_error_page;
|
||||
}
|
||||
got_it:
|
||||
if ((map.m_flags & F2FS_MAP_MAPPED)) {
|
||||
block_nr = map.m_pblk + block_in_file - map.m_lblk;
|
||||
SetPageMappedToDisk(page);
|
||||
|
||||
if (!PageUptodate(page) && !cleancache_get_page(page)) {
|
||||
SetPageUptodate(page);
|
||||
goto confused;
|
||||
}
|
||||
} else {
|
||||
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
goto next_page;
|
||||
}
|
||||
|
||||
/*
|
||||
* This page will go to BIO. Do we need to send this
|
||||
* BIO off first?
|
||||
*/
|
||||
if (bio && (last_block_in_bio != block_nr - 1)) {
|
||||
submit_and_realloc:
|
||||
submit_bio(READ, bio);
|
||||
bio = NULL;
|
||||
}
|
||||
if (bio == NULL) {
|
||||
struct f2fs_crypto_ctx *ctx = NULL;
|
||||
|
||||
if (f2fs_encrypted_inode(inode) &&
|
||||
S_ISREG(inode->i_mode)) {
|
||||
struct page *cpage;
|
||||
|
||||
ctx = f2fs_get_crypto_ctx(inode);
|
||||
if (IS_ERR(ctx))
|
||||
goto set_error_page;
|
||||
|
||||
/* wait the page to be moved by cleaning */
|
||||
cpage = find_lock_page(
|
||||
META_MAPPING(F2FS_I_SB(inode)),
|
||||
block_nr);
|
||||
if (cpage) {
|
||||
f2fs_wait_on_page_writeback(cpage,
|
||||
DATA);
|
||||
f2fs_put_page(cpage, 1);
|
||||
}
|
||||
}
|
||||
|
||||
bio = bio_alloc(GFP_KERNEL,
|
||||
min_t(int, nr_pages, bio_get_nr_vecs(bdev)));
|
||||
if (!bio) {
|
||||
if (ctx)
|
||||
f2fs_release_crypto_ctx(ctx);
|
||||
goto set_error_page;
|
||||
}
|
||||
bio->bi_bdev = bdev;
|
||||
bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(block_nr);
|
||||
bio->bi_end_io = f2fs_read_end_io;
|
||||
bio->bi_private = ctx;
|
||||
}
|
||||
|
||||
if (bio_add_page(bio, page, blocksize, 0) < blocksize)
|
||||
goto submit_and_realloc;
|
||||
|
||||
last_block_in_bio = block_nr;
|
||||
goto next_page;
|
||||
set_error_page:
|
||||
SetPageError(page);
|
||||
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
|
||||
unlock_page(page);
|
||||
goto next_page;
|
||||
confused:
|
||||
if (bio) {
|
||||
submit_bio(READ, bio);
|
||||
bio = NULL;
|
||||
}
|
||||
unlock_page(page);
|
||||
next_page:
|
||||
if (pages)
|
||||
page_cache_release(page);
|
||||
}
|
||||
BUG_ON(pages && !list_empty(pages));
|
||||
if (bio)
|
||||
submit_bio(READ, bio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f2fs_read_data_page(struct file *file, struct page *page)
|
||||
@ -1350,8 +1601,7 @@ static int f2fs_read_data_page(struct file *file, struct page *page)
|
||||
if (f2fs_has_inline_data(inode))
|
||||
ret = f2fs_read_inline_data(inode, page);
|
||||
if (ret == -EAGAIN)
|
||||
ret = mpage_readpage(page, get_data_block);
|
||||
|
||||
ret = f2fs_mpage_readpages(page->mapping, NULL, page, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1365,11 +1615,12 @@ static int f2fs_read_data_pages(struct file *file,
|
||||
if (f2fs_has_inline_data(inode))
|
||||
return 0;
|
||||
|
||||
return mpage_readpages(mapping, pages, nr_pages, get_data_block);
|
||||
return f2fs_mpage_readpages(mapping, pages, NULL, nr_pages);
|
||||
}
|
||||
|
||||
int do_write_data_page(struct page *page, struct f2fs_io_info *fio)
|
||||
int do_write_data_page(struct f2fs_io_info *fio)
|
||||
{
|
||||
struct page *page = fio->page;
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct dnode_of_data dn;
|
||||
int err = 0;
|
||||
@ -1387,6 +1638,14 @@ int do_write_data_page(struct page *page, struct f2fs_io_info *fio)
|
||||
goto out_writepage;
|
||||
}
|
||||
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
|
||||
fio->encrypted_page = f2fs_encrypt(inode, fio->page);
|
||||
if (IS_ERR(fio->encrypted_page)) {
|
||||
err = PTR_ERR(fio->encrypted_page);
|
||||
goto out_writepage;
|
||||
}
|
||||
}
|
||||
|
||||
set_page_writeback(page);
|
||||
|
||||
/*
|
||||
@ -1396,11 +1655,11 @@ int do_write_data_page(struct page *page, struct f2fs_io_info *fio)
|
||||
if (unlikely(fio->blk_addr != NEW_ADDR &&
|
||||
!is_cold_data(page) &&
|
||||
need_inplace_update(inode))) {
|
||||
rewrite_data_page(page, fio);
|
||||
rewrite_data_page(fio);
|
||||
set_inode_flag(F2FS_I(inode), FI_UPDATE_WRITE);
|
||||
trace_f2fs_do_write_data_page(page, IPU);
|
||||
} else {
|
||||
write_data_page(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);
|
||||
@ -1425,8 +1684,11 @@ static int f2fs_write_data_page(struct page *page,
|
||||
bool need_balance_fs = false;
|
||||
int err = 0;
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = sbi,
|
||||
.type = DATA,
|
||||
.rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
|
||||
trace_f2fs_writepage(page, DATA);
|
||||
@ -1456,7 +1718,7 @@ write:
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
goto redirty_out;
|
||||
err = do_write_data_page(page, &fio);
|
||||
err = do_write_data_page(&fio);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -1476,7 +1738,7 @@ write:
|
||||
if (f2fs_has_inline_data(inode))
|
||||
err = f2fs_write_inline_data(inode, page);
|
||||
if (err == -EAGAIN)
|
||||
err = do_write_data_page(page, &fio);
|
||||
err = do_write_data_page(&fio);
|
||||
f2fs_unlock_op(sbi);
|
||||
done:
|
||||
if (err && err != -ENOENT)
|
||||
@ -1645,11 +1907,14 @@ put_next:
|
||||
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
|
||||
} else {
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = sbi,
|
||||
.type = DATA,
|
||||
.rw = READ_SYNC,
|
||||
.blk_addr = dn.data_blkaddr,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
err = f2fs_submit_page_bio(sbi, page, &fio);
|
||||
err = f2fs_submit_page_bio(&fio);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
@ -1663,6 +1928,15 @@ put_next:
|
||||
f2fs_put_page(page, 1);
|
||||
goto repeat;
|
||||
}
|
||||
|
||||
/* avoid symlink page */
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
|
||||
err = f2fs_decrypt_one(inode, page);
|
||||
if (err) {
|
||||
f2fs_put_page(page, 1);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
SetPageUptodate(page);
|
||||
@ -1733,6 +2007,9 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
|
||||
return err;
|
||||
}
|
||||
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
return 0;
|
||||
|
||||
if (check_direct_IO(inode, iter, offset))
|
||||
return 0;
|
||||
|
||||
|
@ -94,7 +94,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
||||
static void update_sit_info(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_stat_info *si = F2FS_STAT(sbi);
|
||||
unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist;
|
||||
unsigned long long blks_per_sec, hblks_per_sec, total_vblocks;
|
||||
unsigned long long bimodal, dist;
|
||||
unsigned int segno, vblocks;
|
||||
int ndirty = 0;
|
||||
|
||||
@ -112,10 +113,10 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
|
||||
ndirty++;
|
||||
}
|
||||
}
|
||||
dist = MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec / 100;
|
||||
si->bimodal = bimodal / dist;
|
||||
dist = div_u64(MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec, 100);
|
||||
si->bimodal = div_u64(bimodal, dist);
|
||||
if (si->dirty_count)
|
||||
si->avg_vblocks = total_vblocks / ndirty;
|
||||
si->avg_vblocks = div_u64(total_vblocks, ndirty);
|
||||
else
|
||||
si->avg_vblocks = 0;
|
||||
}
|
||||
@ -143,7 +144,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
||||
si->base_mem += sizeof(struct sit_info);
|
||||
si->base_mem += MAIN_SEGS(sbi) * sizeof(struct seg_entry);
|
||||
si->base_mem += f2fs_bitmap_size(MAIN_SEGS(sbi));
|
||||
si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi);
|
||||
si->base_mem += 3 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi);
|
||||
si->base_mem += SIT_VBLOCK_MAP_SIZE;
|
||||
if (sbi->segs_per_sec > 1)
|
||||
si->base_mem += MAIN_SECS(sbi) * sizeof(struct sec_entry);
|
||||
|
196
fs/f2fs/dir.c
196
fs/f2fs/dir.c
@ -76,20 +76,10 @@ static unsigned long dir_block_index(unsigned int level,
|
||||
return bidx;
|
||||
}
|
||||
|
||||
static bool early_match_name(size_t namelen, f2fs_hash_t namehash,
|
||||
struct f2fs_dir_entry *de)
|
||||
{
|
||||
if (le16_to_cpu(de->name_len) != namelen)
|
||||
return false;
|
||||
|
||||
if (de->hash_code != namehash)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
||||
struct qstr *name, int *max_slots,
|
||||
struct f2fs_filename *fname,
|
||||
f2fs_hash_t namehash,
|
||||
int *max_slots,
|
||||
struct page **res_page)
|
||||
{
|
||||
struct f2fs_dentry_block *dentry_blk;
|
||||
@ -98,9 +88,8 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
||||
|
||||
dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page);
|
||||
|
||||
make_dentry_ptr(&d, (void *)dentry_blk, 1);
|
||||
de = find_target_dentry(name, max_slots, &d);
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
|
||||
de = find_target_dentry(fname, namehash, max_slots, &d);
|
||||
if (de)
|
||||
*res_page = dentry_page;
|
||||
else
|
||||
@ -114,13 +103,15 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
||||
return de;
|
||||
}
|
||||
|
||||
struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots,
|
||||
struct f2fs_dentry_ptr *d)
|
||||
struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname,
|
||||
f2fs_hash_t namehash, int *max_slots,
|
||||
struct f2fs_dentry_ptr *d)
|
||||
{
|
||||
struct f2fs_dir_entry *de;
|
||||
unsigned long bit_pos = 0;
|
||||
f2fs_hash_t namehash = f2fs_dentry_hash(name);
|
||||
int max_len = 0;
|
||||
struct f2fs_str de_name = FSTR_INIT(NULL, 0);
|
||||
struct f2fs_str *name = &fname->disk_name;
|
||||
|
||||
if (max_slots)
|
||||
*max_slots = 0;
|
||||
@ -132,8 +123,18 @@ struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots,
|
||||
}
|
||||
|
||||
de = &d->dentry[bit_pos];
|
||||
if (early_match_name(name->len, namehash, de) &&
|
||||
!memcmp(d->filename[bit_pos], name->name, name->len))
|
||||
|
||||
/* encrypted case */
|
||||
de_name.name = d->filename[bit_pos];
|
||||
de_name.len = le16_to_cpu(de->name_len);
|
||||
|
||||
/* show encrypted name */
|
||||
if (fname->hash) {
|
||||
if (de->hash_code == fname->hash)
|
||||
goto found;
|
||||
} else if (de_name.len == name->len &&
|
||||
de->hash_code == namehash &&
|
||||
!memcmp(de_name.name, name->name, name->len))
|
||||
goto found;
|
||||
|
||||
if (max_slots && max_len > *max_slots)
|
||||
@ -155,16 +156,21 @@ found:
|
||||
}
|
||||
|
||||
static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
||||
unsigned int level, struct qstr *name,
|
||||
f2fs_hash_t namehash, struct page **res_page)
|
||||
unsigned int level,
|
||||
struct f2fs_filename *fname,
|
||||
struct page **res_page)
|
||||
{
|
||||
int s = GET_DENTRY_SLOTS(name->len);
|
||||
struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
|
||||
int s = GET_DENTRY_SLOTS(name.len);
|
||||
unsigned int nbucket, nblock;
|
||||
unsigned int bidx, end_block;
|
||||
struct page *dentry_page;
|
||||
struct f2fs_dir_entry *de = NULL;
|
||||
bool room = false;
|
||||
int max_slots;
|
||||
f2fs_hash_t namehash;
|
||||
|
||||
namehash = f2fs_dentry_hash(&name);
|
||||
|
||||
f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH);
|
||||
|
||||
@ -177,13 +183,14 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
||||
|
||||
for (; bidx < end_block; bidx++) {
|
||||
/* no need to allocate new dentry pages to all the indices */
|
||||
dentry_page = find_data_page(dir, bidx, true);
|
||||
dentry_page = find_data_page(dir, bidx);
|
||||
if (IS_ERR(dentry_page)) {
|
||||
room = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
de = find_in_block(dentry_page, name, &max_slots, res_page);
|
||||
de = find_in_block(dentry_page, fname, namehash, &max_slots,
|
||||
res_page);
|
||||
if (de)
|
||||
break;
|
||||
|
||||
@ -211,30 +218,34 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
|
||||
{
|
||||
unsigned long npages = dir_blocks(dir);
|
||||
struct f2fs_dir_entry *de = NULL;
|
||||
f2fs_hash_t name_hash;
|
||||
unsigned int max_depth;
|
||||
unsigned int level;
|
||||
struct f2fs_filename fname;
|
||||
int err;
|
||||
|
||||
*res_page = NULL;
|
||||
|
||||
if (f2fs_has_inline_dentry(dir))
|
||||
return find_in_inline_dir(dir, child, res_page);
|
||||
|
||||
if (npages == 0)
|
||||
err = f2fs_fname_setup_filename(dir, child, 1, &fname);
|
||||
if (err)
|
||||
return NULL;
|
||||
|
||||
name_hash = f2fs_dentry_hash(child);
|
||||
if (f2fs_has_inline_dentry(dir)) {
|
||||
de = find_in_inline_dir(dir, &fname, res_page);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (npages == 0)
|
||||
goto out;
|
||||
|
||||
max_depth = F2FS_I(dir)->i_current_depth;
|
||||
|
||||
for (level = 0; level < max_depth; level++) {
|
||||
de = find_in_level(dir, level, child, name_hash, res_page);
|
||||
de = find_in_level(dir, level, &fname, res_page);
|
||||
if (de)
|
||||
break;
|
||||
}
|
||||
if (!de && F2FS_I(dir)->chash != name_hash) {
|
||||
F2FS_I(dir)->chash = name_hash;
|
||||
F2FS_I(dir)->clevel = level - 1;
|
||||
}
|
||||
out:
|
||||
f2fs_fname_free_filename(&fname);
|
||||
return de;
|
||||
}
|
||||
|
||||
@ -303,10 +314,14 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage)
|
||||
set_page_dirty(ipage);
|
||||
}
|
||||
|
||||
int update_dent_inode(struct inode *inode, const struct qstr *name)
|
||||
int update_dent_inode(struct inode *inode, struct inode *to,
|
||||
const struct qstr *name)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
if (file_enc_name(to))
|
||||
return 0;
|
||||
|
||||
page = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
@ -356,7 +371,7 @@ static int make_empty_dir(struct inode *inode,
|
||||
|
||||
dentry_blk = kmap_atomic(dentry_page);
|
||||
|
||||
make_dentry_ptr(&d, (void *)dentry_blk, 1);
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
|
||||
do_make_empty_dir(inode, parent, &d);
|
||||
|
||||
kunmap_atomic(dentry_blk);
|
||||
@ -390,6 +405,12 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir,
|
||||
err = f2fs_init_security(inode, dir, name, page);
|
||||
if (err)
|
||||
goto put_error;
|
||||
|
||||
if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) {
|
||||
err = f2fs_inherit_context(dir, inode, page);
|
||||
if (err)
|
||||
goto put_error;
|
||||
}
|
||||
} else {
|
||||
page = get_node_page(F2FS_I_SB(dir), inode->i_ino);
|
||||
if (IS_ERR(page))
|
||||
@ -501,24 +522,33 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
|
||||
unsigned long bidx, block;
|
||||
f2fs_hash_t dentry_hash;
|
||||
unsigned int nbucket, nblock;
|
||||
size_t namelen = name->len;
|
||||
struct page *dentry_page = NULL;
|
||||
struct f2fs_dentry_block *dentry_blk = NULL;
|
||||
struct f2fs_dentry_ptr d;
|
||||
int slots = GET_DENTRY_SLOTS(namelen);
|
||||
struct page *page = NULL;
|
||||
int err = 0;
|
||||
struct f2fs_filename fname;
|
||||
struct qstr new_name;
|
||||
int slots, err;
|
||||
|
||||
err = f2fs_fname_setup_filename(dir, name, 0, &fname);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
new_name.name = fname_name(&fname);
|
||||
new_name.len = fname_len(&fname);
|
||||
|
||||
if (f2fs_has_inline_dentry(dir)) {
|
||||
err = f2fs_add_inline_entry(dir, name, inode, ino, mode);
|
||||
err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode);
|
||||
if (!err || err != -EAGAIN)
|
||||
return err;
|
||||
goto out;
|
||||
else
|
||||
err = 0;
|
||||
}
|
||||
|
||||
dentry_hash = f2fs_dentry_hash(name);
|
||||
level = 0;
|
||||
slots = GET_DENTRY_SLOTS(new_name.len);
|
||||
dentry_hash = f2fs_dentry_hash(&new_name);
|
||||
|
||||
current_depth = F2FS_I(dir)->i_current_depth;
|
||||
if (F2FS_I(dir)->chash == dentry_hash) {
|
||||
level = F2FS_I(dir)->clevel;
|
||||
@ -526,8 +556,10 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
|
||||
}
|
||||
|
||||
start:
|
||||
if (unlikely(current_depth == MAX_DIR_HASH_DEPTH))
|
||||
return -ENOSPC;
|
||||
if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) {
|
||||
err = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Increase the depth, if required */
|
||||
if (level == current_depth)
|
||||
@ -541,8 +573,10 @@ start:
|
||||
|
||||
for (block = bidx; block <= (bidx + nblock - 1); block++) {
|
||||
dentry_page = get_new_data_page(dir, NULL, block, true);
|
||||
if (IS_ERR(dentry_page))
|
||||
return PTR_ERR(dentry_page);
|
||||
if (IS_ERR(dentry_page)) {
|
||||
err = PTR_ERR(dentry_page);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dentry_blk = kmap(dentry_page);
|
||||
bit_pos = room_for_filename(&dentry_blk->dentry_bitmap,
|
||||
@ -562,15 +596,17 @@ add_dentry:
|
||||
|
||||
if (inode) {
|
||||
down_write(&F2FS_I(inode)->i_sem);
|
||||
page = init_inode_metadata(inode, dir, name, NULL);
|
||||
page = init_inode_metadata(inode, dir, &new_name, NULL);
|
||||
if (IS_ERR(page)) {
|
||||
err = PTR_ERR(page);
|
||||
goto fail;
|
||||
}
|
||||
if (f2fs_encrypted_inode(dir))
|
||||
file_set_enc_name(inode);
|
||||
}
|
||||
|
||||
make_dentry_ptr(&d, (void *)dentry_blk, 1);
|
||||
f2fs_update_dentry(ino, mode, &d, name, dentry_hash, bit_pos);
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
|
||||
f2fs_update_dentry(ino, mode, &d, &new_name, dentry_hash, bit_pos);
|
||||
|
||||
set_page_dirty(dentry_page);
|
||||
|
||||
@ -592,6 +628,8 @@ fail:
|
||||
}
|
||||
kunmap(dentry_page);
|
||||
f2fs_put_page(dentry_page, 1);
|
||||
out:
|
||||
f2fs_fname_free_filename(&fname);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -729,11 +767,12 @@ bool f2fs_empty_dir(struct inode *dir)
|
||||
}
|
||||
|
||||
bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
|
||||
unsigned int start_pos)
|
||||
unsigned int start_pos, struct f2fs_str *fstr)
|
||||
{
|
||||
unsigned char d_type = DT_UNKNOWN;
|
||||
unsigned int bit_pos;
|
||||
struct f2fs_dir_entry *de = NULL;
|
||||
struct f2fs_str de_name = FSTR_INIT(NULL, 0);
|
||||
|
||||
bit_pos = ((unsigned long)ctx->pos % d->max);
|
||||
|
||||
@ -747,8 +786,24 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
|
||||
d_type = f2fs_filetype_table[de->file_type];
|
||||
else
|
||||
d_type = DT_UNKNOWN;
|
||||
if (!dir_emit(ctx, d->filename[bit_pos],
|
||||
le16_to_cpu(de->name_len),
|
||||
|
||||
/* encrypted case */
|
||||
de_name.name = d->filename[bit_pos];
|
||||
de_name.len = le16_to_cpu(de->name_len);
|
||||
|
||||
if (f2fs_encrypted_inode(d->inode)) {
|
||||
int save_len = fstr->len;
|
||||
int ret;
|
||||
|
||||
ret = f2fs_fname_disk_to_usr(d->inode, &de->hash_code,
|
||||
&de_name, fstr);
|
||||
de_name = *fstr;
|
||||
fstr->len = save_len;
|
||||
if (ret < 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!dir_emit(ctx, de_name.name, de_name.len,
|
||||
le32_to_cpu(de->ino), d_type))
|
||||
return true;
|
||||
|
||||
@ -767,9 +822,24 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
|
||||
struct file_ra_state *ra = &file->f_ra;
|
||||
unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK);
|
||||
struct f2fs_dentry_ptr d;
|
||||
struct f2fs_str fstr = FSTR_INIT(NULL, 0);
|
||||
int err = 0;
|
||||
|
||||
if (f2fs_has_inline_dentry(inode))
|
||||
return f2fs_read_inline_dir(file, ctx);
|
||||
if (f2fs_encrypted_inode(inode)) {
|
||||
err = f2fs_get_encryption_info(inode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = f2fs_fname_crypto_alloc_buffer(inode, F2FS_NAME_LEN,
|
||||
&fstr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (f2fs_has_inline_dentry(inode)) {
|
||||
err = f2fs_read_inline_dir(file, ctx, &fstr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* readahead for multi pages of dir */
|
||||
if (npages - n > 1 && !ra_has_index(ra, n))
|
||||
@ -783,9 +853,9 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
|
||||
|
||||
dentry_blk = kmap(dentry_page);
|
||||
|
||||
make_dentry_ptr(&d, (void *)dentry_blk, 1);
|
||||
make_dentry_ptr(inode, &d, (void *)dentry_blk, 1);
|
||||
|
||||
if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK))
|
||||
if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr))
|
||||
goto stop;
|
||||
|
||||
ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK;
|
||||
@ -798,8 +868,9 @@ stop:
|
||||
kunmap(dentry_page);
|
||||
f2fs_put_page(dentry_page, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
f2fs_fname_crypto_free_buffer(&fstr);
|
||||
return err;
|
||||
}
|
||||
|
||||
const struct file_operations f2fs_dir_operations = {
|
||||
@ -808,4 +879,7 @@ const struct file_operations f2fs_dir_operations = {
|
||||
.iterate = f2fs_readdir,
|
||||
.fsync = f2fs_sync_file,
|
||||
.unlocked_ioctl = f2fs_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = f2fs_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
320
fs/f2fs/f2fs.h
320
fs/f2fs/f2fs.h
@ -70,6 +70,15 @@ struct f2fs_mount_info {
|
||||
unsigned int opt;
|
||||
};
|
||||
|
||||
#define F2FS_FEATURE_ENCRYPT 0x0001
|
||||
|
||||
#define F2FS_HAS_FEATURE(sb, mask) \
|
||||
((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0)
|
||||
#define F2FS_SET_FEATURE(sb, mask) \
|
||||
F2FS_SB(sb)->raw_super->feature |= cpu_to_le32(mask)
|
||||
#define F2FS_CLEAR_FEATURE(sb, mask) \
|
||||
F2FS_SB(sb)->raw_super->feature &= ~cpu_to_le32(mask)
|
||||
|
||||
#define CRCPOLY_LE 0xedb88320
|
||||
|
||||
static inline __u32 f2fs_crc32(void *buf, size_t len)
|
||||
@ -110,6 +119,8 @@ enum {
|
||||
#define DEF_BATCHED_TRIM_SECTIONS 32
|
||||
#define BATCHED_TRIM_SEGMENTS(sbi) \
|
||||
(SM_I(sbi)->trim_sections * (sbi)->segs_per_sec)
|
||||
#define BATCHED_TRIM_BLOCKS(sbi) \
|
||||
(BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg)
|
||||
|
||||
struct cp_control {
|
||||
int reason;
|
||||
@ -218,6 +229,13 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
|
||||
#define F2FS_IOC_RELEASE_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 4)
|
||||
#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5)
|
||||
|
||||
#define F2FS_IOC_SET_ENCRYPTION_POLICY \
|
||||
_IOR('f', 19, struct f2fs_encryption_policy)
|
||||
#define F2FS_IOC_GET_ENCRYPTION_PWSALT \
|
||||
_IOW('f', 20, __u8[16])
|
||||
#define F2FS_IOC_GET_ENCRYPTION_POLICY \
|
||||
_IOW('f', 21, struct f2fs_encryption_policy)
|
||||
|
||||
/*
|
||||
* should be same as XFS_IOC_GOINGDOWN.
|
||||
* Flags for going down operation used by FS_IOC_GOINGDOWN
|
||||
@ -239,16 +257,38 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
|
||||
* For INODE and NODE manager
|
||||
*/
|
||||
/* for directory operations */
|
||||
struct f2fs_str {
|
||||
unsigned char *name;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
struct f2fs_filename {
|
||||
const struct qstr *usr_fname;
|
||||
struct f2fs_str disk_name;
|
||||
f2fs_hash_t hash;
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
struct f2fs_str crypto_buf;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define FSTR_INIT(n, l) { .name = n, .len = l }
|
||||
#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len)
|
||||
#define fname_name(p) ((p)->disk_name.name)
|
||||
#define fname_len(p) ((p)->disk_name.len)
|
||||
|
||||
struct f2fs_dentry_ptr {
|
||||
struct inode *inode;
|
||||
const void *bitmap;
|
||||
struct f2fs_dir_entry *dentry;
|
||||
__u8 (*filename)[F2FS_SLOT_LEN];
|
||||
int max;
|
||||
};
|
||||
|
||||
static inline void make_dentry_ptr(struct f2fs_dentry_ptr *d,
|
||||
void *src, int type)
|
||||
static inline void make_dentry_ptr(struct inode *inode,
|
||||
struct f2fs_dentry_ptr *d, void *src, int type)
|
||||
{
|
||||
d->inode = inode;
|
||||
|
||||
if (type == 1) {
|
||||
struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src;
|
||||
d->max = NR_DENTRY_IN_BLOCK;
|
||||
@ -314,11 +354,52 @@ struct extent_tree {
|
||||
unsigned int count; /* # of extent node in rb-tree*/
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure is taken from ext4_map_blocks.
|
||||
*
|
||||
* Note that, however, f2fs uses NEW and MAPPED flags for f2fs_map_blocks().
|
||||
*/
|
||||
#define F2FS_MAP_NEW (1 << BH_New)
|
||||
#define F2FS_MAP_MAPPED (1 << BH_Mapped)
|
||||
#define F2FS_MAP_UNWRITTEN (1 << BH_Unwritten)
|
||||
#define F2FS_MAP_FLAGS (F2FS_MAP_NEW | F2FS_MAP_MAPPED |\
|
||||
F2FS_MAP_UNWRITTEN)
|
||||
|
||||
struct f2fs_map_blocks {
|
||||
block_t m_pblk;
|
||||
block_t m_lblk;
|
||||
unsigned int m_len;
|
||||
unsigned int m_flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* i_advise uses FADVISE_XXX_BIT. We can add additional hints later.
|
||||
*/
|
||||
#define FADVISE_COLD_BIT 0x01
|
||||
#define FADVISE_LOST_PINO_BIT 0x02
|
||||
#define FADVISE_ENCRYPT_BIT 0x04
|
||||
#define FADVISE_ENC_NAME_BIT 0x08
|
||||
|
||||
#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_set_cold(inode) set_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_lost_pino(inode) set_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_clear_cold(inode) clear_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_got_pino(inode) clear_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_is_encrypt(inode) is_file(inode, FADVISE_ENCRYPT_BIT)
|
||||
#define file_set_encrypt(inode) set_file(inode, FADVISE_ENCRYPT_BIT)
|
||||
#define file_clear_encrypt(inode) clear_file(inode, FADVISE_ENCRYPT_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)
|
||||
|
||||
/* 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
|
||||
|
||||
@ -346,6 +427,11 @@ struct f2fs_inode_info {
|
||||
struct radix_tree_root inmem_root; /* radix tree for inmem pages */
|
||||
struct list_head inmem_pages; /* inmemory pages managed by f2fs */
|
||||
struct mutex inmem_lock; /* lock for inmemory pages */
|
||||
|
||||
#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,
|
||||
@ -571,9 +657,12 @@ enum page_type {
|
||||
};
|
||||
|
||||
struct f2fs_io_info {
|
||||
struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */
|
||||
enum page_type type; /* contains DATA/NODE/META/META_FLUSH */
|
||||
int rw; /* contains R/RS/W/WS with REQ_META/REQ_PRIO */
|
||||
block_t blk_addr; /* block address to be written */
|
||||
struct page *page; /* page to be written */
|
||||
struct page *encrypted_page; /* encrypted page */
|
||||
};
|
||||
|
||||
#define is_read_io(rw) (((rw) & 1) == READ)
|
||||
@ -666,6 +755,7 @@ struct f2fs_sb_info {
|
||||
block_t user_block_count; /* # of user blocks */
|
||||
block_t total_valid_block_count; /* # of valid blocks */
|
||||
block_t alloc_valid_block_count; /* # of allocated blocks */
|
||||
block_t discard_blks; /* discard command candidats */
|
||||
block_t last_valid_block_count; /* for recovery */
|
||||
u32 s_next_generation; /* for NFS support */
|
||||
atomic_t nr_pages[NR_COUNT_TYPE]; /* # of pages, see count_type */
|
||||
@ -1193,6 +1283,24 @@ static inline int f2fs_test_bit(unsigned int nr, char *addr)
|
||||
return mask & *addr;
|
||||
}
|
||||
|
||||
static inline void f2fs_set_bit(unsigned int nr, char *addr)
|
||||
{
|
||||
int mask;
|
||||
|
||||
addr += (nr >> 3);
|
||||
mask = 1 << (7 - (nr & 0x07));
|
||||
*addr |= mask;
|
||||
}
|
||||
|
||||
static inline void f2fs_clear_bit(unsigned int nr, char *addr)
|
||||
{
|
||||
int mask;
|
||||
|
||||
addr += (nr >> 3);
|
||||
mask = 1 << (7 - (nr & 0x07));
|
||||
*addr &= ~mask;
|
||||
}
|
||||
|
||||
static inline int f2fs_test_and_set_bit(unsigned int nr, char *addr)
|
||||
{
|
||||
int mask;
|
||||
@ -1391,6 +1499,21 @@ static inline void f2fs_dentry_kunmap(struct inode *dir, struct page *page)
|
||||
kunmap(page);
|
||||
}
|
||||
|
||||
static inline int is_file(struct inode *inode, int type)
|
||||
{
|
||||
return F2FS_I(inode)->i_advise & type;
|
||||
}
|
||||
|
||||
static inline void set_file(struct inode *inode, int type)
|
||||
{
|
||||
F2FS_I(inode)->i_advise |= type;
|
||||
}
|
||||
|
||||
static inline void clear_file(struct inode *inode, int type)
|
||||
{
|
||||
F2FS_I(inode)->i_advise &= ~type;
|
||||
}
|
||||
|
||||
static inline int f2fs_readonly(struct super_block *sb)
|
||||
{
|
||||
return sb->s_flags & MS_RDONLY;
|
||||
@ -1407,6 +1530,17 @@ static inline void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi)
|
||||
sbi->sb->s_flags |= MS_RDONLY;
|
||||
}
|
||||
|
||||
static inline bool 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;
|
||||
}
|
||||
|
||||
#define get_inode_mode(i) \
|
||||
((is_inode_flag_set(F2FS_I(i), FI_ACL_MODE)) ? \
|
||||
(F2FS_I(i)->i_acl_mode) : ((i)->i_mode))
|
||||
@ -1453,10 +1587,11 @@ struct dentry *f2fs_get_parent(struct dentry *child);
|
||||
*/
|
||||
extern unsigned char f2fs_filetype_table[F2FS_FT_MAX];
|
||||
void set_de_type(struct f2fs_dir_entry *, umode_t);
|
||||
struct f2fs_dir_entry *find_target_dentry(struct qstr *, int *,
|
||||
struct f2fs_dentry_ptr *);
|
||||
|
||||
struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *,
|
||||
f2fs_hash_t, int *, struct f2fs_dentry_ptr *);
|
||||
bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *,
|
||||
unsigned int);
|
||||
unsigned int, struct f2fs_str *);
|
||||
void do_make_empty_dir(struct inode *, struct inode *,
|
||||
struct f2fs_dentry_ptr *);
|
||||
struct page *init_inode_metadata(struct inode *, struct inode *,
|
||||
@ -1470,7 +1605,7 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **);
|
||||
ino_t f2fs_inode_by_name(struct inode *, struct qstr *);
|
||||
void f2fs_set_link(struct inode *, struct f2fs_dir_entry *,
|
||||
struct page *, struct inode *);
|
||||
int update_dent_inode(struct inode *, const struct qstr *);
|
||||
int update_dent_inode(struct inode *, struct inode *, const struct qstr *);
|
||||
void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *,
|
||||
const struct qstr *, f2fs_hash_t , unsigned int);
|
||||
int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t,
|
||||
@ -1478,7 +1613,6 @@ int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t,
|
||||
void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *,
|
||||
struct inode *);
|
||||
int f2fs_do_tmpfile(struct inode *, struct inode *);
|
||||
int f2fs_make_empty(struct inode *, struct inode *);
|
||||
bool f2fs_empty_dir(struct inode *);
|
||||
|
||||
static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
|
||||
@ -1490,6 +1624,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
|
||||
/*
|
||||
* super.c
|
||||
*/
|
||||
int f2fs_commit_super(struct f2fs_sb_info *, bool);
|
||||
int f2fs_sync_fs(struct super_block *, int);
|
||||
extern __printf(3, 4)
|
||||
void f2fs_msg(struct super_block *, const char *, const char *, ...);
|
||||
@ -1506,8 +1641,8 @@ struct dnode_of_data;
|
||||
struct node_info;
|
||||
|
||||
bool available_free_memory(struct f2fs_sb_info *, int);
|
||||
int need_dentry_mark(struct f2fs_sb_info *, nid_t);
|
||||
bool is_checkpointed_node(struct f2fs_sb_info *, nid_t);
|
||||
bool has_fsynced_inode(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 *);
|
||||
int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int);
|
||||
@ -1548,21 +1683,20 @@ int create_flush_cmd_control(struct f2fs_sb_info *);
|
||||
void destroy_flush_cmd_control(struct f2fs_sb_info *);
|
||||
void invalidate_blocks(struct f2fs_sb_info *, block_t);
|
||||
void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t);
|
||||
void clear_prefree_segments(struct f2fs_sb_info *);
|
||||
void clear_prefree_segments(struct f2fs_sb_info *, struct cp_control *);
|
||||
void release_discard_addrs(struct f2fs_sb_info *);
|
||||
void discard_next_dnode(struct f2fs_sb_info *, block_t);
|
||||
int npages_for_summary_flush(struct f2fs_sb_info *, bool);
|
||||
void allocate_new_segments(struct f2fs_sb_info *);
|
||||
int f2fs_trim_fs(struct f2fs_sb_info *, struct fstrim_range *);
|
||||
struct page *get_sum_page(struct f2fs_sb_info *, unsigned int);
|
||||
void update_meta_page(struct f2fs_sb_info *, void *, block_t);
|
||||
void write_meta_page(struct f2fs_sb_info *, struct page *);
|
||||
void write_node_page(struct f2fs_sb_info *, struct page *,
|
||||
unsigned int, struct f2fs_io_info *);
|
||||
void write_data_page(struct page *, struct dnode_of_data *,
|
||||
struct f2fs_io_info *);
|
||||
void rewrite_data_page(struct page *, struct f2fs_io_info *);
|
||||
void recover_data_page(struct f2fs_sb_info *, struct page *,
|
||||
struct f2fs_summary *, block_t, block_t);
|
||||
void write_node_page(unsigned int, struct f2fs_io_info *);
|
||||
void write_data_page(struct dnode_of_data *, struct f2fs_io_info *);
|
||||
void rewrite_data_page(struct f2fs_io_info *);
|
||||
void f2fs_replace_block(struct f2fs_sb_info *, struct dnode_of_data *,
|
||||
block_t, block_t, unsigned char, bool);
|
||||
void allocate_data_block(struct f2fs_sb_info *, struct page *,
|
||||
block_t, block_t *, struct f2fs_summary *, int);
|
||||
void f2fs_wait_on_page_writeback(struct page *, enum page_type);
|
||||
@ -1581,6 +1715,7 @@ void destroy_segment_manager_caches(void);
|
||||
*/
|
||||
struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t);
|
||||
struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t);
|
||||
bool is_valid_blkaddr(struct f2fs_sb_info *, block_t, int);
|
||||
int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int);
|
||||
void ra_meta_pages_cond(struct f2fs_sb_info *, pgoff_t);
|
||||
long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long);
|
||||
@ -1607,10 +1742,8 @@ void destroy_checkpoint_caches(void);
|
||||
* data.c
|
||||
*/
|
||||
void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int);
|
||||
int f2fs_submit_page_bio(struct f2fs_sb_info *, struct page *,
|
||||
struct f2fs_io_info *);
|
||||
void f2fs_submit_page_mbio(struct f2fs_sb_info *, struct page *,
|
||||
struct f2fs_io_info *);
|
||||
int f2fs_submit_page_bio(struct f2fs_io_info *);
|
||||
void f2fs_submit_page_mbio(struct f2fs_io_info *);
|
||||
void set_data_blkaddr(struct dnode_of_data *);
|
||||
int reserve_new_block(struct dnode_of_data *);
|
||||
int f2fs_reserve_block(struct dnode_of_data *, pgoff_t);
|
||||
@ -1619,10 +1752,11 @@ void f2fs_destroy_extent_tree(struct inode *);
|
||||
void f2fs_init_extent_cache(struct inode *, struct f2fs_extent *);
|
||||
void f2fs_update_extent_cache(struct dnode_of_data *);
|
||||
void f2fs_preserve_extent_tree(struct inode *);
|
||||
struct page *find_data_page(struct inode *, pgoff_t, bool);
|
||||
struct page *get_read_data_page(struct inode *, pgoff_t, int);
|
||||
struct page *find_data_page(struct inode *, pgoff_t);
|
||||
struct page *get_lock_data_page(struct inode *, pgoff_t);
|
||||
struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool);
|
||||
int do_write_data_page(struct page *, struct f2fs_io_info *);
|
||||
int do_write_data_page(struct f2fs_io_info *);
|
||||
int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64);
|
||||
void init_extent_cache_info(struct f2fs_sb_info *);
|
||||
int __init create_extent_cache(void);
|
||||
@ -1787,13 +1921,15 @@ extern const struct address_space_operations f2fs_node_aops;
|
||||
extern const struct address_space_operations f2fs_meta_aops;
|
||||
extern const struct inode_operations f2fs_dir_inode_operations;
|
||||
extern const struct inode_operations f2fs_symlink_inode_operations;
|
||||
extern const struct inode_operations f2fs_encrypted_symlink_inode_operations;
|
||||
extern const struct inode_operations f2fs_special_inode_operations;
|
||||
extern struct kmem_cache *inode_entry_slab;
|
||||
|
||||
/*
|
||||
* inline.c
|
||||
*/
|
||||
bool f2fs_may_inline(struct inode *);
|
||||
bool f2fs_may_inline_data(struct inode *);
|
||||
bool f2fs_may_inline_dentry(struct inode *);
|
||||
void read_inline_data(struct page *, struct page *);
|
||||
bool truncate_inline_inode(struct page *, u64);
|
||||
int f2fs_read_inline_data(struct inode *, struct page *);
|
||||
@ -1801,8 +1937,8 @@ int f2fs_convert_inline_page(struct dnode_of_data *, struct page *);
|
||||
int f2fs_convert_inline_inode(struct inode *);
|
||||
int f2fs_write_inline_data(struct inode *, struct page *);
|
||||
bool recover_inline_data(struct inode *, struct page *);
|
||||
struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct qstr *,
|
||||
struct page **);
|
||||
struct f2fs_dir_entry *find_in_inline_dir(struct inode *,
|
||||
struct f2fs_filename *, struct page **);
|
||||
struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **);
|
||||
int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *);
|
||||
int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *,
|
||||
@ -1810,5 +1946,137 @@ int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *,
|
||||
void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *,
|
||||
struct inode *, struct inode *);
|
||||
bool f2fs_empty_inline_dir(struct inode *);
|
||||
int f2fs_read_inline_dir(struct file *, struct dir_context *);
|
||||
int f2fs_read_inline_dir(struct file *, struct dir_context *,
|
||||
struct f2fs_str *);
|
||||
|
||||
/*
|
||||
* crypto support
|
||||
*/
|
||||
static inline int f2fs_encrypted_inode(struct inode *inode)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
return file_is_encrypt(inode);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void f2fs_set_encrypted_inode(struct inode *inode)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
file_set_encrypt(inode);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool f2fs_bio_encrypted(struct bio *bio)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
return unlikely(bio->bi_private != NULL);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int f2fs_sb_has_crypto(struct super_block *sb)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool f2fs_may_encrypt(struct inode *inode)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
mode_t mode = inode->i_mode;
|
||||
|
||||
return (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode));
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* crypto_policy.c */
|
||||
int f2fs_is_child_context_consistent_with_parent(struct inode *,
|
||||
struct inode *);
|
||||
int f2fs_inherit_context(struct inode *, struct inode *, struct page *);
|
||||
int f2fs_process_policy(const struct f2fs_encryption_policy *, struct inode *);
|
||||
int f2fs_get_policy(struct inode *, struct f2fs_encryption_policy *);
|
||||
|
||||
/* crypt.c */
|
||||
extern struct kmem_cache *f2fs_crypt_info_cachep;
|
||||
bool f2fs_valid_contents_enc_mode(uint32_t);
|
||||
uint32_t f2fs_validate_encryption_key_size(uint32_t, uint32_t);
|
||||
struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *);
|
||||
void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *);
|
||||
struct page *f2fs_encrypt(struct inode *, struct page *);
|
||||
int f2fs_decrypt(struct f2fs_crypto_ctx *, struct page *);
|
||||
int f2fs_decrypt_one(struct inode *, struct page *);
|
||||
void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *, struct bio *);
|
||||
|
||||
/* crypto_key.c */
|
||||
void f2fs_free_encryption_info(struct inode *, struct f2fs_crypt_info *);
|
||||
int _f2fs_get_encryption_info(struct inode *inode);
|
||||
|
||||
/* crypto_fname.c */
|
||||
bool f2fs_valid_filenames_enc_mode(uint32_t);
|
||||
u32 f2fs_fname_crypto_round_up(u32, u32);
|
||||
int f2fs_fname_crypto_alloc_buffer(struct inode *, u32, struct f2fs_str *);
|
||||
int f2fs_fname_disk_to_usr(struct inode *, f2fs_hash_t *,
|
||||
const struct f2fs_str *, struct f2fs_str *);
|
||||
int f2fs_fname_usr_to_disk(struct inode *, const struct qstr *,
|
||||
struct f2fs_str *);
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
void f2fs_restore_and_release_control_page(struct page **);
|
||||
void f2fs_restore_control_page(struct page *);
|
||||
|
||||
int __init f2fs_init_crypto(void);
|
||||
int f2fs_crypto_initialize(void);
|
||||
void f2fs_exit_crypto(void);
|
||||
|
||||
int f2fs_has_encryption_key(struct inode *);
|
||||
|
||||
static inline int f2fs_get_encryption_info(struct inode *inode)
|
||||
{
|
||||
struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
|
||||
|
||||
if (!ci ||
|
||||
(ci->ci_keyring_key &&
|
||||
(ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
|
||||
(1 << KEY_FLAG_REVOKED) |
|
||||
(1 << KEY_FLAG_DEAD)))))
|
||||
return _f2fs_get_encryption_info(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f2fs_fname_crypto_free_buffer(struct f2fs_str *);
|
||||
int f2fs_fname_setup_filename(struct inode *, const struct qstr *,
|
||||
int lookup, struct f2fs_filename *);
|
||||
void f2fs_fname_free_filename(struct f2fs_filename *);
|
||||
#else
|
||||
static inline void f2fs_restore_and_release_control_page(struct page **p) { }
|
||||
static inline void f2fs_restore_control_page(struct page *p) { }
|
||||
|
||||
static inline int __init f2fs_init_crypto(void) { return 0; }
|
||||
static inline void f2fs_exit_crypto(void) { }
|
||||
|
||||
static inline int f2fs_has_encryption_key(struct inode *i) { return 0; }
|
||||
static inline int f2fs_get_encryption_info(struct inode *i) { return 0; }
|
||||
static inline void f2fs_fname_crypto_free_buffer(struct f2fs_str *p) { }
|
||||
|
||||
static inline int f2fs_fname_setup_filename(struct inode *dir,
|
||||
const struct qstr *iname,
|
||||
int lookup, struct f2fs_filename *fname)
|
||||
{
|
||||
memset(fname, 0, sizeof(struct f2fs_filename));
|
||||
fname->usr_fname = iname;
|
||||
fname->disk_name.name = (unsigned char *)iname->name;
|
||||
fname->disk_name.len = iname->len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void f2fs_fname_free_filename(struct f2fs_filename *fname) { }
|
||||
#endif
|
||||
#endif
|
||||
|
151
fs/f2fs/f2fs_crypto.h
Normal file
151
fs/f2fs/f2fs_crypto.h
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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_ablkcipher *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 */
|
516
fs/f2fs/file.c
516
fs/f2fs/file.c
@ -20,6 +20,7 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/pagevec.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@ -105,7 +106,7 @@ static int get_parent_ino(struct inode *inode, nid_t *pino)
|
||||
if (!dentry)
|
||||
return 0;
|
||||
|
||||
if (update_dent_inode(inode, &dentry->d_name)) {
|
||||
if (update_dent_inode(inode, inode, &dentry->d_name)) {
|
||||
dput(dentry);
|
||||
return 0;
|
||||
}
|
||||
@ -122,6 +123,8 @@ static inline bool need_do_checkpoint(struct inode *inode)
|
||||
|
||||
if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1)
|
||||
need_cp = true;
|
||||
else if (file_enc_name(inode) && need_dentry_mark(sbi, inode->i_ino))
|
||||
need_cp = true;
|
||||
else if (file_wrong_pino(inode))
|
||||
need_cp = true;
|
||||
else if (!space_for_roll_forward(sbi))
|
||||
@ -271,7 +274,7 @@ flush_out:
|
||||
ret = f2fs_issue_flush(sbi);
|
||||
out:
|
||||
trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret);
|
||||
f2fs_trace_ios(NULL, NULL, 1);
|
||||
f2fs_trace_ios(NULL, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -407,6 +410,12 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
|
||||
if (f2fs_encrypted_inode(inode)) {
|
||||
int err = f2fs_get_encryption_info(inode);
|
||||
if (err)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we don't need to use inline_data strictly */
|
||||
if (f2fs_has_inline_data(inode)) {
|
||||
int err = f2fs_convert_inline_inode(inode);
|
||||
@ -419,6 +428,18 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f2fs_file_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
int ret = generic_file_open(inode, filp);
|
||||
|
||||
if (!ret && f2fs_encrypted_inode(inode)) {
|
||||
ret = f2fs_get_encryption_info(inode);
|
||||
if (ret)
|
||||
ret = -EACCES;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
|
||||
{
|
||||
int nr_free = 0, ofs = dn->ofs_in_node;
|
||||
@ -461,28 +482,32 @@ void truncate_data_blocks(struct dnode_of_data *dn)
|
||||
}
|
||||
|
||||
static int truncate_partial_data_page(struct inode *inode, u64 from,
|
||||
bool force)
|
||||
bool cache_only)
|
||||
{
|
||||
unsigned offset = from & (PAGE_CACHE_SIZE - 1);
|
||||
pgoff_t index = from >> PAGE_CACHE_SHIFT;
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct page *page;
|
||||
|
||||
if (!offset && !force)
|
||||
if (!offset && !cache_only)
|
||||
return 0;
|
||||
|
||||
page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, force);
|
||||
if (cache_only) {
|
||||
page = grab_cache_page(mapping, index);
|
||||
if (page && PageUptodate(page))
|
||||
goto truncate_out;
|
||||
f2fs_put_page(page, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
page = get_lock_data_page(inode, index);
|
||||
if (IS_ERR(page))
|
||||
return 0;
|
||||
|
||||
lock_page(page);
|
||||
if (unlikely(!PageUptodate(page) ||
|
||||
page->mapping != inode->i_mapping))
|
||||
goto out;
|
||||
|
||||
truncate_out:
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
zero_user(page, offset, PAGE_CACHE_SIZE - offset);
|
||||
if (!force)
|
||||
if (!cache_only || !f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode))
|
||||
set_page_dirty(page);
|
||||
out:
|
||||
f2fs_put_page(page, 1);
|
||||
return 0;
|
||||
}
|
||||
@ -560,7 +585,7 @@ void f2fs_truncate(struct inode *inode)
|
||||
trace_f2fs_truncate(inode);
|
||||
|
||||
/* we should check inline_data size */
|
||||
if (f2fs_has_inline_data(inode) && !f2fs_may_inline(inode)) {
|
||||
if (f2fs_has_inline_data(inode) && !f2fs_may_inline_data(inode)) {
|
||||
if (f2fs_convert_inline_inode(inode))
|
||||
return;
|
||||
}
|
||||
@ -622,16 +647,20 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
return err;
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
if (attr->ia_size != i_size_read(inode)) {
|
||||
if (f2fs_encrypted_inode(inode) &&
|
||||
f2fs_get_encryption_info(inode))
|
||||
return -EACCES;
|
||||
|
||||
if (attr->ia_size <= i_size_read(inode)) {
|
||||
truncate_setsize(inode, attr->ia_size);
|
||||
f2fs_truncate(inode);
|
||||
f2fs_balance_fs(F2FS_I_SB(inode));
|
||||
} else {
|
||||
/*
|
||||
* giving a chance to truncate blocks past EOF which
|
||||
* are fallocated with FALLOC_FL_KEEP_SIZE.
|
||||
* do not trim all blocks after i_size if target size is
|
||||
* larger than i_size.
|
||||
*/
|
||||
f2fs_truncate(inode);
|
||||
truncate_setsize(inode, attr->ia_size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -718,10 +747,6 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len)
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* skip punching hole beyond i_size */
|
||||
if (offset >= inode->i_size)
|
||||
return ret;
|
||||
|
||||
if (f2fs_has_inline_data(inode)) {
|
||||
ret = f2fs_convert_inline_inode(inode);
|
||||
if (ret)
|
||||
@ -765,6 +790,320 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int f2fs_do_collapse(struct inode *inode, pgoff_t start, pgoff_t end)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct dnode_of_data dn;
|
||||
pgoff_t nrpages = (i_size_read(inode) + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
int ret = 0;
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
for (; end < nrpages; start++, end++) {
|
||||
block_t new_addr, old_addr;
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
ret = get_dnode_of_data(&dn, end, LOOKUP_NODE_RA);
|
||||
if (ret && ret != -ENOENT) {
|
||||
goto out;
|
||||
} else if (ret == -ENOENT) {
|
||||
new_addr = NULL_ADDR;
|
||||
} else {
|
||||
new_addr = dn.data_blkaddr;
|
||||
truncate_data_blocks_range(&dn, 1);
|
||||
f2fs_put_dnode(&dn);
|
||||
}
|
||||
|
||||
if (new_addr == NULL_ADDR) {
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
ret = get_dnode_of_data(&dn, start, LOOKUP_NODE_RA);
|
||||
if (ret && ret != -ENOENT)
|
||||
goto out;
|
||||
else if (ret == -ENOENT)
|
||||
continue;
|
||||
|
||||
if (dn.data_blkaddr == NULL_ADDR) {
|
||||
f2fs_put_dnode(&dn);
|
||||
continue;
|
||||
} else {
|
||||
truncate_data_blocks_range(&dn, 1);
|
||||
}
|
||||
|
||||
f2fs_put_dnode(&dn);
|
||||
} else {
|
||||
struct page *ipage;
|
||||
|
||||
ipage = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(ipage)) {
|
||||
ret = PTR_ERR(ipage);
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_new_dnode(&dn, inode, ipage, NULL, 0);
|
||||
ret = f2fs_reserve_block(&dn, start);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
old_addr = dn.data_blkaddr;
|
||||
if (old_addr != NEW_ADDR && new_addr == NEW_ADDR) {
|
||||
dn.data_blkaddr = NULL_ADDR;
|
||||
f2fs_update_extent_cache(&dn);
|
||||
invalidate_blocks(sbi, old_addr);
|
||||
|
||||
dn.data_blkaddr = new_addr;
|
||||
set_data_blkaddr(&dn);
|
||||
} else if (new_addr != NEW_ADDR) {
|
||||
struct node_info ni;
|
||||
|
||||
get_node_info(sbi, dn.nid, &ni);
|
||||
f2fs_replace_block(sbi, &dn, old_addr, new_addr,
|
||||
ni.version, true);
|
||||
}
|
||||
|
||||
f2fs_put_dnode(&dn);
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
f2fs_unlock_op(sbi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len)
|
||||
{
|
||||
pgoff_t pg_start, pg_end;
|
||||
loff_t new_size;
|
||||
int ret;
|
||||
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
return -EINVAL;
|
||||
|
||||
if (offset + len >= i_size_read(inode))
|
||||
return -EINVAL;
|
||||
|
||||
/* collapse range should be aligned to block size of f2fs. */
|
||||
if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1))
|
||||
return -EINVAL;
|
||||
|
||||
pg_start = offset >> PAGE_CACHE_SHIFT;
|
||||
pg_end = (offset + len) >> PAGE_CACHE_SHIFT;
|
||||
|
||||
/* write out all dirty pages from offset */
|
||||
ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
truncate_pagecache(inode, offset);
|
||||
|
||||
ret = f2fs_do_collapse(inode, pg_start, pg_end);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
new_size = i_size_read(inode) - len;
|
||||
|
||||
ret = truncate_blocks(inode, new_size, true);
|
||||
if (!ret)
|
||||
i_size_write(inode, new_size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len,
|
||||
int mode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
pgoff_t index, pg_start, pg_end;
|
||||
loff_t new_size = i_size_read(inode);
|
||||
loff_t off_start, off_end;
|
||||
int ret = 0;
|
||||
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
return -EINVAL;
|
||||
|
||||
ret = inode_newsize_ok(inode, (len + offset));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
if (f2fs_has_inline_data(inode)) {
|
||||
ret = f2fs_convert_inline_inode(inode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = filemap_write_and_wait_range(mapping, offset, offset + len - 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
truncate_pagecache_range(inode, offset, offset + len - 1);
|
||||
|
||||
pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT;
|
||||
pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT;
|
||||
|
||||
off_start = offset & (PAGE_CACHE_SIZE - 1);
|
||||
off_end = (offset + len) & (PAGE_CACHE_SIZE - 1);
|
||||
|
||||
if (pg_start == pg_end) {
|
||||
fill_zero(inode, pg_start, off_start, off_end - off_start);
|
||||
if (offset + len > new_size)
|
||||
new_size = offset + len;
|
||||
new_size = max_t(loff_t, new_size, offset + len);
|
||||
} else {
|
||||
if (off_start) {
|
||||
fill_zero(inode, pg_start++, off_start,
|
||||
PAGE_CACHE_SIZE - off_start);
|
||||
new_size = max_t(loff_t, new_size,
|
||||
pg_start << PAGE_CACHE_SHIFT);
|
||||
}
|
||||
|
||||
for (index = pg_start; index < pg_end; index++) {
|
||||
struct dnode_of_data dn;
|
||||
struct page *ipage;
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
ipage = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(ipage)) {
|
||||
ret = PTR_ERR(ipage);
|
||||
f2fs_unlock_op(sbi);
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_new_dnode(&dn, inode, ipage, NULL, 0);
|
||||
ret = f2fs_reserve_block(&dn, index);
|
||||
if (ret) {
|
||||
f2fs_unlock_op(sbi);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dn.data_blkaddr != NEW_ADDR) {
|
||||
invalidate_blocks(sbi, dn.data_blkaddr);
|
||||
|
||||
dn.data_blkaddr = NEW_ADDR;
|
||||
set_data_blkaddr(&dn);
|
||||
|
||||
dn.data_blkaddr = NULL_ADDR;
|
||||
f2fs_update_extent_cache(&dn);
|
||||
}
|
||||
f2fs_put_dnode(&dn);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
new_size = max_t(loff_t, new_size,
|
||||
(index + 1) << PAGE_CACHE_SHIFT);
|
||||
}
|
||||
|
||||
if (off_end) {
|
||||
fill_zero(inode, pg_end, 0, off_end);
|
||||
new_size = max_t(loff_t, new_size, offset + len);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) {
|
||||
i_size_write(inode, new_size);
|
||||
mark_inode_dirty(inode);
|
||||
update_inode_page(inode);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
pgoff_t pg_start, pg_end, delta, nrpages, idx;
|
||||
loff_t new_size;
|
||||
int ret;
|
||||
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
return -EINVAL;
|
||||
|
||||
new_size = i_size_read(inode) + len;
|
||||
if (new_size > inode->i_sb->s_maxbytes)
|
||||
return -EFBIG;
|
||||
|
||||
if (offset >= i_size_read(inode))
|
||||
return -EINVAL;
|
||||
|
||||
/* insert range should be aligned to block size of f2fs. */
|
||||
if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1))
|
||||
return -EINVAL;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
ret = truncate_blocks(inode, i_size_read(inode), true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* write out all dirty pages from offset */
|
||||
ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
truncate_pagecache(inode, offset);
|
||||
|
||||
pg_start = offset >> PAGE_CACHE_SHIFT;
|
||||
pg_end = (offset + len) >> PAGE_CACHE_SHIFT;
|
||||
delta = pg_end - pg_start;
|
||||
nrpages = (i_size_read(inode) + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
for (idx = nrpages - 1; idx >= pg_start && idx != -1; idx--) {
|
||||
struct dnode_of_data dn;
|
||||
struct page *ipage;
|
||||
block_t new_addr, old_addr;
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
ret = get_dnode_of_data(&dn, idx, LOOKUP_NODE_RA);
|
||||
if (ret && ret != -ENOENT) {
|
||||
goto out;
|
||||
} else if (ret == -ENOENT) {
|
||||
goto next;
|
||||
} else if (dn.data_blkaddr == NULL_ADDR) {
|
||||
f2fs_put_dnode(&dn);
|
||||
goto next;
|
||||
} else {
|
||||
new_addr = dn.data_blkaddr;
|
||||
truncate_data_blocks_range(&dn, 1);
|
||||
f2fs_put_dnode(&dn);
|
||||
}
|
||||
|
||||
ipage = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(ipage)) {
|
||||
ret = PTR_ERR(ipage);
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_new_dnode(&dn, inode, ipage, NULL, 0);
|
||||
ret = f2fs_reserve_block(&dn, idx + delta);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
old_addr = dn.data_blkaddr;
|
||||
f2fs_bug_on(sbi, old_addr != NEW_ADDR);
|
||||
|
||||
if (new_addr != NEW_ADDR) {
|
||||
struct node_info ni;
|
||||
|
||||
get_node_info(sbi, dn.nid, &ni);
|
||||
f2fs_replace_block(sbi, &dn, old_addr, new_addr,
|
||||
ni.version, true);
|
||||
}
|
||||
f2fs_put_dnode(&dn);
|
||||
next:
|
||||
f2fs_unlock_op(sbi);
|
||||
}
|
||||
|
||||
i_size_write(inode, new_size);
|
||||
return 0;
|
||||
out:
|
||||
f2fs_unlock_op(sbi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int expand_inode_data(struct inode *inode, loff_t offset,
|
||||
loff_t len, int mode)
|
||||
{
|
||||
@ -830,23 +1169,40 @@ static long f2fs_fallocate(struct file *file, int mode,
|
||||
loff_t offset, loff_t len)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
long ret;
|
||||
long ret = 0;
|
||||
|
||||
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
|
||||
if (f2fs_encrypted_inode(inode) &&
|
||||
(mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
|
||||
FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE |
|
||||
FALLOC_FL_INSERT_RANGE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
if (mode & FALLOC_FL_PUNCH_HOLE)
|
||||
if (mode & FALLOC_FL_PUNCH_HOLE) {
|
||||
if (offset >= inode->i_size)
|
||||
goto out;
|
||||
|
||||
ret = punch_hole(inode, offset, len);
|
||||
else
|
||||
} else if (mode & FALLOC_FL_COLLAPSE_RANGE) {
|
||||
ret = f2fs_collapse_range(inode, offset, len);
|
||||
} else if (mode & FALLOC_FL_ZERO_RANGE) {
|
||||
ret = f2fs_zero_range(inode, offset, len, mode);
|
||||
} else if (mode & FALLOC_FL_INSERT_RANGE) {
|
||||
ret = f2fs_insert_range(inode, offset, len);
|
||||
} else {
|
||||
ret = expand_inode_data(inode, offset, len, mode);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
trace_f2fs_fallocate(inode, mode, offset, len, ret);
|
||||
@ -1035,11 +1391,9 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp)
|
||||
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
|
||||
}
|
||||
|
||||
if (f2fs_is_volatile_file(inode)) {
|
||||
if (f2fs_is_volatile_file(inode))
|
||||
clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
|
||||
filemap_fdatawrite(inode->i_mapping);
|
||||
set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
|
||||
}
|
||||
|
||||
mnt_drop_write_file(filp);
|
||||
return ret;
|
||||
}
|
||||
@ -1109,6 +1463,86 @@ static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool uuid_is_nonzero(__u8 u[16])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
if (u[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
struct f2fs_encryption_policy policy;
|
||||
struct inode *inode = file_inode(filp);
|
||||
|
||||
if (copy_from_user(&policy, (struct f2fs_encryption_policy __user *)arg,
|
||||
sizeof(policy)))
|
||||
return -EFAULT;
|
||||
|
||||
return f2fs_process_policy(&policy, inode);
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
struct f2fs_encryption_policy policy;
|
||||
struct inode *inode = file_inode(filp);
|
||||
int err;
|
||||
|
||||
err = f2fs_get_policy(inode, &policy);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (copy_to_user((struct f2fs_encryption_policy __user *)arg, &policy,
|
||||
sizeof(policy)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
int err;
|
||||
|
||||
if (!f2fs_sb_has_crypto(inode->i_sb))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (uuid_is_nonzero(sbi->raw_super->encrypt_pw_salt))
|
||||
goto got_it;
|
||||
|
||||
err = mnt_want_write_file(filp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* update superblock with uuid */
|
||||
generate_random_uuid(sbi->raw_super->encrypt_pw_salt);
|
||||
|
||||
err = f2fs_commit_super(sbi, false);
|
||||
|
||||
mnt_drop_write_file(filp);
|
||||
if (err) {
|
||||
/* undo new data */
|
||||
memset(sbi->raw_super->encrypt_pw_salt, 0, 16);
|
||||
return err;
|
||||
}
|
||||
got_it:
|
||||
if (copy_to_user((__u8 __user *)arg, sbi->raw_super->encrypt_pw_salt,
|
||||
16))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
@ -1132,11 +1566,29 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
return f2fs_ioc_shutdown(filp, arg);
|
||||
case FITRIM:
|
||||
return f2fs_ioc_fitrim(filp, arg);
|
||||
case F2FS_IOC_SET_ENCRYPTION_POLICY:
|
||||
return f2fs_ioc_set_encryption_policy(filp, arg);
|
||||
case F2FS_IOC_GET_ENCRYPTION_POLICY:
|
||||
return f2fs_ioc_get_encryption_policy(filp, arg);
|
||||
case F2FS_IOC_GET_ENCRYPTION_PWSALT:
|
||||
return f2fs_ioc_get_encryption_pwsalt(filp, arg);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct inode *inode = file_inode(iocb->ki_filp);
|
||||
|
||||
if (f2fs_encrypted_inode(inode) &&
|
||||
!f2fs_has_encryption_key(inode) &&
|
||||
f2fs_get_encryption_info(inode))
|
||||
return -EACCES;
|
||||
|
||||
return generic_file_write_iter(iocb, from);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
@ -1157,8 +1609,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
const struct file_operations f2fs_file_operations = {
|
||||
.llseek = f2fs_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
.write_iter = generic_file_write_iter,
|
||||
.open = generic_file_open,
|
||||
.write_iter = f2fs_file_write_iter,
|
||||
.open = f2fs_file_open,
|
||||
.release = f2fs_release_file,
|
||||
.mmap = f2fs_file_mmap,
|
||||
.fsync = f2fs_sync_file,
|
||||
|
115
fs/f2fs/gc.c
115
fs/f2fs/gc.c
@ -518,12 +518,79 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void move_data_page(struct inode *inode, struct page *page, int gc_type)
|
||||
static void move_encrypted_block(struct inode *inode, block_t bidx)
|
||||
{
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(inode),
|
||||
.type = DATA,
|
||||
.rw = WRITE_SYNC,
|
||||
.rw = READ_SYNC,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
struct dnode_of_data dn;
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
struct page *page;
|
||||
int err;
|
||||
|
||||
/* do not read out */
|
||||
page = grab_cache_page(inode->i_mapping, bidx);
|
||||
if (!page)
|
||||
return;
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (unlikely(dn.data_blkaddr == NULL_ADDR))
|
||||
goto put_out;
|
||||
|
||||
get_node_info(fio.sbi, dn.nid, &ni);
|
||||
set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
|
||||
|
||||
/* read page */
|
||||
fio.page = page;
|
||||
fio.blk_addr = dn.data_blkaddr;
|
||||
|
||||
fio.encrypted_page = grab_cache_page(META_MAPPING(fio.sbi), fio.blk_addr);
|
||||
if (!fio.encrypted_page)
|
||||
goto put_out;
|
||||
|
||||
f2fs_submit_page_bio(&fio);
|
||||
|
||||
/* allocate block address */
|
||||
f2fs_wait_on_page_writeback(dn.node_page, NODE);
|
||||
|
||||
allocate_data_block(fio.sbi, NULL, fio.blk_addr,
|
||||
&fio.blk_addr, &sum, CURSEG_COLD_DATA);
|
||||
dn.data_blkaddr = fio.blk_addr;
|
||||
|
||||
/* write page */
|
||||
lock_page(fio.encrypted_page);
|
||||
set_page_writeback(fio.encrypted_page);
|
||||
fio.rw = WRITE_SYNC;
|
||||
f2fs_submit_page_mbio(&fio);
|
||||
|
||||
set_data_blkaddr(&dn);
|
||||
f2fs_update_extent_cache(&dn);
|
||||
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
|
||||
if (page->index == 0)
|
||||
set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN);
|
||||
|
||||
f2fs_put_page(fio.encrypted_page, 1);
|
||||
put_out:
|
||||
f2fs_put_dnode(&dn);
|
||||
out:
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
page = get_lock_data_page(inode, bidx);
|
||||
if (IS_ERR(page))
|
||||
return;
|
||||
|
||||
if (gc_type == BG_GC) {
|
||||
if (PageWriteback(page))
|
||||
@ -531,12 +598,19 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type)
|
||||
set_page_dirty(page);
|
||||
set_cold_data(page);
|
||||
} else {
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(inode),
|
||||
.type = DATA,
|
||||
.rw = WRITE_SYNC,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
|
||||
if (clear_page_dirty_for_io(page))
|
||||
inode_dec_dirty_pages(inode);
|
||||
set_cold_data(page);
|
||||
do_write_data_page(page, &fio);
|
||||
do_write_data_page(&fio);
|
||||
clear_cold_data(page);
|
||||
}
|
||||
out:
|
||||
@ -599,10 +673,16 @@ next_step:
|
||||
if (IS_ERR(inode) || is_bad_inode(inode))
|
||||
continue;
|
||||
|
||||
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode));
|
||||
/* if encrypted inode, let's go phase 3 */
|
||||
if (f2fs_encrypted_inode(inode) &&
|
||||
S_ISREG(inode->i_mode)) {
|
||||
add_gc_inode(gc_list, inode);
|
||||
continue;
|
||||
}
|
||||
|
||||
data_page = find_data_page(inode,
|
||||
start_bidx + ofs_in_node, false);
|
||||
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode));
|
||||
data_page = get_read_data_page(inode,
|
||||
start_bidx + ofs_in_node, READA);
|
||||
if (IS_ERR(data_page)) {
|
||||
iput(inode);
|
||||
continue;
|
||||
@ -616,12 +696,12 @@ next_step:
|
||||
/* phase 3 */
|
||||
inode = find_gc_inode(gc_list, dni.ino);
|
||||
if (inode) {
|
||||
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode));
|
||||
data_page = get_lock_data_page(inode,
|
||||
start_bidx + ofs_in_node);
|
||||
if (IS_ERR(data_page))
|
||||
continue;
|
||||
move_data_page(inode, data_page, gc_type);
|
||||
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode))
|
||||
+ ofs_in_node;
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
move_encrypted_block(inode, start_bidx);
|
||||
else
|
||||
move_data_page(inode, start_bidx, gc_type);
|
||||
stat_inc_data_blk_count(sbi, 1, gc_type);
|
||||
}
|
||||
}
|
||||
@ -670,6 +750,15 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
|
||||
|
||||
sum = page_address(sum_page);
|
||||
|
||||
/*
|
||||
* this is to avoid deadlock:
|
||||
* - lock_page(sum_page) - f2fs_replace_block
|
||||
* - check_valid_map() - mutex_lock(sentry_lock)
|
||||
* - mutex_lock(sentry_lock) - change_curseg()
|
||||
* - lock_page(sum_page)
|
||||
*/
|
||||
unlock_page(sum_page);
|
||||
|
||||
switch (GET_SUM_TYPE((&sum->footer))) {
|
||||
case SUM_TYPE_NODE:
|
||||
gc_node_segment(sbi, sum->entries, segno, gc_type);
|
||||
@ -683,7 +772,7 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
|
||||
stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)), gc_type);
|
||||
stat_inc_call_count(sbi->stat_info);
|
||||
|
||||
f2fs_put_page(sum_page, 1);
|
||||
f2fs_put_page(sum_page, 0);
|
||||
}
|
||||
|
||||
int f2fs_gc(struct f2fs_sb_info *sbi)
|
||||
|
@ -79,8 +79,7 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info)
|
||||
const unsigned char *name = name_info->name;
|
||||
size_t len = name_info->len;
|
||||
|
||||
if ((len <= 2) && (name[0] == '.') &&
|
||||
(name[1] == '.' || name[1] == '\0'))
|
||||
if (is_dot_dotdot(name_info))
|
||||
return 0;
|
||||
|
||||
/* Initialize the default seed for the hash checksum functions */
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#include "f2fs.h"
|
||||
|
||||
bool f2fs_may_inline(struct inode *inode)
|
||||
bool f2fs_may_inline_data(struct inode *inode)
|
||||
{
|
||||
if (!test_opt(F2FS_I_SB(inode), INLINE_DATA))
|
||||
return false;
|
||||
@ -27,6 +27,20 @@ bool f2fs_may_inline(struct inode *inode)
|
||||
if (i_size_read(inode) > MAX_INLINE_DATA)
|
||||
return false;
|
||||
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool f2fs_may_inline_dentry(struct inode *inode)
|
||||
{
|
||||
if (!test_opt(F2FS_I_SB(inode), INLINE_DENTRY))
|
||||
return false;
|
||||
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -95,8 +109,11 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
|
||||
{
|
||||
void *src_addr, *dst_addr;
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(dn->inode),
|
||||
.type = DATA,
|
||||
.rw = WRITE_SYNC | REQ_PRIO,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
int dirty, err;
|
||||
|
||||
@ -130,7 +147,7 @@ no_update:
|
||||
/* write data page to try to make data consistent */
|
||||
set_page_writeback(page);
|
||||
fio.blk_addr = dn->data_blkaddr;
|
||||
write_data_page(page, dn, &fio);
|
||||
write_data_page(dn, &fio);
|
||||
set_data_blkaddr(dn);
|
||||
f2fs_update_extent_cache(dn);
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
@ -267,23 +284,26 @@ process_inline:
|
||||
}
|
||||
|
||||
struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
|
||||
struct qstr *name, struct page **res_page)
|
||||
struct f2fs_filename *fname, struct page **res_page)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
|
||||
struct f2fs_inline_dentry *inline_dentry;
|
||||
struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
|
||||
struct f2fs_dir_entry *de;
|
||||
struct f2fs_dentry_ptr d;
|
||||
struct page *ipage;
|
||||
f2fs_hash_t namehash;
|
||||
|
||||
ipage = get_node_page(sbi, dir->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return NULL;
|
||||
|
||||
namehash = f2fs_dentry_hash(&name);
|
||||
|
||||
inline_dentry = inline_data_addr(ipage);
|
||||
|
||||
make_dentry_ptr(&d, (void *)inline_dentry, 2);
|
||||
de = find_target_dentry(name, NULL, &d);
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2);
|
||||
de = find_target_dentry(fname, namehash, NULL, &d);
|
||||
unlock_page(ipage);
|
||||
if (de)
|
||||
*res_page = ipage;
|
||||
@ -325,7 +345,7 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent,
|
||||
|
||||
dentry_blk = inline_data_addr(ipage);
|
||||
|
||||
make_dentry_ptr(&d, (void *)dentry_blk, 2);
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
||||
do_make_empty_dir(inode, parent, &d);
|
||||
|
||||
set_page_dirty(ipage);
|
||||
@ -429,7 +449,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
|
||||
name_hash = f2fs_dentry_hash(name);
|
||||
make_dentry_ptr(&d, (void *)dentry_blk, 2);
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
||||
f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos);
|
||||
|
||||
set_page_dirty(ipage);
|
||||
@ -506,7 +526,8 @@ bool f2fs_empty_inline_dir(struct inode *dir)
|
||||
return true;
|
||||
}
|
||||
|
||||
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 inode *inode = file_inode(file);
|
||||
struct f2fs_inline_dentry *inline_dentry = NULL;
|
||||
@ -522,9 +543,9 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx)
|
||||
|
||||
inline_dentry = inline_data_addr(ipage);
|
||||
|
||||
make_dentry_ptr(&d, (void *)inline_dentry, 2);
|
||||
make_dentry_ptr(inode, &d, (void *)inline_dentry, 2);
|
||||
|
||||
if (!f2fs_fill_dentries(ctx, &d, 0))
|
||||
if (!f2fs_fill_dentries(ctx, &d, 0, fstr))
|
||||
ctx->pos = NR_INLINE_DENTRY;
|
||||
|
||||
f2fs_put_page(ipage, 1);
|
||||
|
@ -198,7 +198,10 @@ make_now:
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
|
||||
} else if (S_ISLNK(inode->i_mode)) {
|
||||
inode->i_op = &f2fs_symlink_inode_operations;
|
||||
if (f2fs_encrypted_inode(inode))
|
||||
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
|
||||
else
|
||||
inode->i_op = &f2fs_symlink_inode_operations;
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
|
||||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
|
||||
@ -359,6 +362,10 @@ no_delete:
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_UPDATE_WRITE))
|
||||
add_dirty_inode(sbi, inode->i_ino, UPDATE_INO);
|
||||
out_clear:
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
if (F2FS_I(inode)->i_crypt_info)
|
||||
f2fs_free_encryption_info(inode, F2FS_I(inode)->i_crypt_info);
|
||||
#endif
|
||||
clear_inode(inode);
|
||||
}
|
||||
|
||||
|
360
fs/f2fs/namei.c
360
fs/f2fs/namei.c
@ -56,11 +56,18 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (f2fs_may_inline(inode))
|
||||
/* If the directory encrypted, then we should encrypt the inode. */
|
||||
if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode))
|
||||
f2fs_set_encrypted_inode(inode);
|
||||
|
||||
if (f2fs_may_inline_data(inode))
|
||||
set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
|
||||
if (test_opt(sbi, INLINE_DENTRY) && S_ISDIR(inode->i_mode))
|
||||
if (f2fs_may_inline_dentry(inode))
|
||||
set_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY);
|
||||
|
||||
stat_inc_inline_inode(inode);
|
||||
stat_inc_inline_dir(inode);
|
||||
|
||||
trace_f2fs_new_inode(inode, 0);
|
||||
mark_inode_dirty(inode);
|
||||
return inode;
|
||||
@ -136,7 +143,6 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
|
||||
alloc_nid_done(sbi, ino);
|
||||
|
||||
stat_inc_inline_inode(inode);
|
||||
d_instantiate(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
|
||||
@ -155,6 +161,10 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
int err;
|
||||
|
||||
if (f2fs_encrypted_inode(dir) &&
|
||||
!f2fs_is_child_context_consistent_with_parent(dir, inode))
|
||||
return -EPERM;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
inode->i_ctime = CURRENT_TIME;
|
||||
@ -232,32 +242,34 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
struct inode *inode = NULL;
|
||||
struct f2fs_dir_entry *de;
|
||||
struct page *page;
|
||||
nid_t ino;
|
||||
int err = 0;
|
||||
|
||||
if (dentry->d_name.len > F2FS_NAME_LEN)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
de = f2fs_find_entry(dir, &dentry->d_name, &page);
|
||||
if (de) {
|
||||
nid_t ino = le32_to_cpu(de->ino);
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
f2fs_put_page(page, 0);
|
||||
if (!de)
|
||||
return d_splice_alias(inode, dentry);
|
||||
|
||||
inode = f2fs_iget(dir->i_sb, ino);
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
ino = le32_to_cpu(de->ino);
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
f2fs_put_page(page, 0);
|
||||
|
||||
if (f2fs_has_inline_dots(inode)) {
|
||||
int err;
|
||||
inode = f2fs_iget(dir->i_sb, ino);
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
|
||||
err = __recover_dot_dentries(inode, dir->i_ino);
|
||||
if (err) {
|
||||
iget_failed(inode);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
}
|
||||
if (f2fs_has_inline_dots(inode)) {
|
||||
err = __recover_dot_dentries(inode, dir->i_ino);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return d_splice_alias(inode, dentry);
|
||||
|
||||
err_out:
|
||||
iget_failed(inode);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
@ -312,16 +324,26 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct inode *inode;
|
||||
size_t symlen = strlen(symname) + 1;
|
||||
size_t len = strlen(symname);
|
||||
size_t p_len;
|
||||
char *p_str;
|
||||
struct f2fs_str disk_link = FSTR_INIT(NULL, 0);
|
||||
struct f2fs_encrypted_symlink_data *sd = NULL;
|
||||
int err;
|
||||
|
||||
if (len > dir->i_sb->s_blocksize)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
|
||||
inode->i_op = &f2fs_symlink_inode_operations;
|
||||
if (f2fs_encrypted_inode(inode))
|
||||
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
|
||||
else
|
||||
inode->i_op = &f2fs_symlink_inode_operations;
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
@ -329,10 +351,46 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
if (err)
|
||||
goto out;
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
err = page_symlink(inode, symname, symlen);
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
|
||||
if (f2fs_encrypted_inode(dir)) {
|
||||
struct qstr istr = QSTR_INIT(symname, len);
|
||||
|
||||
err = f2fs_get_encryption_info(inode);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
err = f2fs_fname_crypto_alloc_buffer(inode, len, &disk_link);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
err = f2fs_fname_usr_to_disk(inode, &istr, &disk_link);
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
|
||||
p_len = encrypted_symlink_data_len(disk_link.len) + 1;
|
||||
|
||||
if (p_len > dir->i_sb->s_blocksize) {
|
||||
err = -ENAMETOOLONG;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
sd = kzalloc(p_len, GFP_NOFS);
|
||||
if (!sd) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
memcpy(sd->encrypted_path, disk_link.name, disk_link.len);
|
||||
sd->len = cpu_to_le16(disk_link.len);
|
||||
p_str = (char *)sd;
|
||||
} else {
|
||||
p_len = len + 1;
|
||||
p_str = (char *)symname;
|
||||
}
|
||||
|
||||
err = page_symlink(inode, p_str, p_len);
|
||||
|
||||
err_out:
|
||||
d_instantiate(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
|
||||
@ -345,10 +403,14 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
* If the symlink path is stored into inline_data, there is no
|
||||
* performance regression.
|
||||
*/
|
||||
filemap_write_and_wait_range(inode->i_mapping, 0, symlen - 1);
|
||||
if (!err)
|
||||
filemap_write_and_wait_range(inode->i_mapping, 0, p_len - 1);
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
|
||||
kfree(sd);
|
||||
f2fs_fname_crypto_free_buffer(&disk_link);
|
||||
return err;
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
@ -379,7 +441,6 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
goto out_fail;
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
stat_inc_inline_dir(inode);
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
@ -441,19 +502,101 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode, struct inode **whiteout)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct inode *inode;
|
||||
int err;
|
||||
|
||||
if (!whiteout)
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
inode = f2fs_new_inode(dir, mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
|
||||
if (whiteout) {
|
||||
init_special_inode(inode, inode->i_mode, WHITEOUT_DEV);
|
||||
inode->i_op = &f2fs_special_inode_operations;
|
||||
} else {
|
||||
inode->i_op = &f2fs_file_inode_operations;
|
||||
inode->i_fop = &f2fs_file_operations;
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
}
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = acquire_orphan_inode(sbi);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = f2fs_do_tmpfile(inode, dir);
|
||||
if (err)
|
||||
goto release_out;
|
||||
|
||||
/*
|
||||
* add this non-linked tmpfile to orphan list, in this way we could
|
||||
* remove all unused data of tmpfile after abnormal power-off.
|
||||
*/
|
||||
add_orphan_inode(sbi, inode->i_ino);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
|
||||
if (whiteout) {
|
||||
inode_dec_link_count(inode);
|
||||
*whiteout = inode;
|
||||
} else {
|
||||
d_tmpfile(dentry, inode);
|
||||
}
|
||||
unlock_new_inode(inode);
|
||||
return 0;
|
||||
|
||||
release_out:
|
||||
release_orphan_inode(sbi);
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
if (f2fs_encrypted_inode(dir)) {
|
||||
int err = f2fs_get_encryption_info(dir);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return __f2fs_tmpfile(dir, dentry, mode, NULL);
|
||||
}
|
||||
|
||||
static int f2fs_create_whiteout(struct inode *dir, struct inode **whiteout)
|
||||
{
|
||||
return __f2fs_tmpfile(dir, NULL, S_IFCHR | WHITEOUT_MODE, whiteout);
|
||||
}
|
||||
|
||||
static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry)
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(old_dir);
|
||||
struct inode *old_inode = d_inode(old_dentry);
|
||||
struct inode *new_inode = d_inode(new_dentry);
|
||||
struct inode *whiteout = NULL;
|
||||
struct page *old_dir_page;
|
||||
struct page *old_page, *new_page;
|
||||
struct page *old_page, *new_page = NULL;
|
||||
struct f2fs_dir_entry *old_dir_entry = NULL;
|
||||
struct f2fs_dir_entry *old_entry;
|
||||
struct f2fs_dir_entry *new_entry;
|
||||
int err = -ENOENT;
|
||||
|
||||
if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) &&
|
||||
!f2fs_is_child_context_consistent_with_parent(new_dir,
|
||||
old_inode)) {
|
||||
err = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
|
||||
@ -467,17 +610,23 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
goto out_old;
|
||||
}
|
||||
|
||||
if (flags & RENAME_WHITEOUT) {
|
||||
err = f2fs_create_whiteout(old_dir, &whiteout);
|
||||
if (err)
|
||||
goto out_dir;
|
||||
}
|
||||
|
||||
if (new_inode) {
|
||||
|
||||
err = -ENOTEMPTY;
|
||||
if (old_dir_entry && !f2fs_empty_dir(new_inode))
|
||||
goto out_dir;
|
||||
goto out_whiteout;
|
||||
|
||||
err = -ENOENT;
|
||||
new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name,
|
||||
&new_page);
|
||||
if (!new_entry)
|
||||
goto out_dir;
|
||||
goto out_whiteout;
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
@ -485,7 +634,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
if (err)
|
||||
goto put_out_dir;
|
||||
|
||||
if (update_dent_inode(old_inode, &new_dentry->d_name)) {
|
||||
if (update_dent_inode(old_inode, new_inode,
|
||||
&new_dentry->d_name)) {
|
||||
release_orphan_inode(sbi);
|
||||
goto put_out_dir;
|
||||
}
|
||||
@ -514,7 +664,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
err = f2fs_add_link(new_dentry, old_inode);
|
||||
if (err) {
|
||||
f2fs_unlock_op(sbi);
|
||||
goto out_dir;
|
||||
goto out_whiteout;
|
||||
}
|
||||
|
||||
if (old_dir_entry) {
|
||||
@ -525,6 +675,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
|
||||
down_write(&F2FS_I(old_inode)->i_sem);
|
||||
file_lost_pino(old_inode);
|
||||
if (new_inode && file_enc_name(new_inode))
|
||||
file_set_enc_name(old_inode);
|
||||
up_write(&F2FS_I(old_inode)->i_sem);
|
||||
|
||||
old_inode->i_ctime = CURRENT_TIME;
|
||||
@ -532,8 +684,18 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
|
||||
f2fs_delete_entry(old_entry, old_page, old_dir, NULL);
|
||||
|
||||
if (whiteout) {
|
||||
whiteout->i_state |= I_LINKABLE;
|
||||
set_inode_flag(F2FS_I(whiteout), FI_INC_LINK);
|
||||
err = f2fs_add_link(old_dentry, whiteout);
|
||||
if (err)
|
||||
goto put_out_dir;
|
||||
whiteout->i_state &= ~I_LINKABLE;
|
||||
iput(whiteout);
|
||||
}
|
||||
|
||||
if (old_dir_entry) {
|
||||
if (old_dir != new_dir) {
|
||||
if (old_dir != new_dir && !whiteout) {
|
||||
f2fs_set_link(old_inode, old_dir_entry,
|
||||
old_dir_page, new_dir);
|
||||
update_inode_page(old_inode);
|
||||
@ -554,8 +716,13 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
|
||||
put_out_dir:
|
||||
f2fs_unlock_op(sbi);
|
||||
f2fs_dentry_kunmap(new_dir, new_page);
|
||||
f2fs_put_page(new_page, 0);
|
||||
if (new_page) {
|
||||
f2fs_dentry_kunmap(new_dir, new_page);
|
||||
f2fs_put_page(new_page, 0);
|
||||
}
|
||||
out_whiteout:
|
||||
if (whiteout)
|
||||
iput(whiteout);
|
||||
out_dir:
|
||||
if (old_dir_entry) {
|
||||
f2fs_dentry_kunmap(old_inode, old_dir_page);
|
||||
@ -581,6 +748,14 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
int old_nlink = 0, new_nlink = 0;
|
||||
int err = -ENOENT;
|
||||
|
||||
if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) &&
|
||||
(old_dir != new_dir) &&
|
||||
(!f2fs_is_child_context_consistent_with_parent(new_dir,
|
||||
old_inode) ||
|
||||
!f2fs_is_child_context_consistent_with_parent(old_dir,
|
||||
new_inode)))
|
||||
return -EPERM;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
|
||||
@ -627,13 +802,17 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
err = update_dent_inode(old_inode, &new_dentry->d_name);
|
||||
err = update_dent_inode(old_inode, new_inode, &new_dentry->d_name);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
if (file_enc_name(new_inode))
|
||||
file_set_enc_name(old_inode);
|
||||
|
||||
err = update_dent_inode(new_inode, &old_dentry->d_name);
|
||||
err = update_dent_inode(new_inode, old_inode, &old_dentry->d_name);
|
||||
if (err)
|
||||
goto out_undo;
|
||||
if (file_enc_name(old_inode))
|
||||
file_set_enc_name(new_inode);
|
||||
|
||||
/* update ".." directory entry info of old dentry */
|
||||
if (old_dir_entry)
|
||||
@ -691,8 +870,11 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
return 0;
|
||||
out_undo:
|
||||
/* Still we may fail to recover name info of f2fs_inode here */
|
||||
update_dent_inode(old_inode, &old_dentry->d_name);
|
||||
/*
|
||||
* Still we may fail to recover name info of f2fs_inode here
|
||||
* Drop it, once its name is set as encrypted
|
||||
*/
|
||||
update_dent_inode(old_inode, old_inode, &old_dentry->d_name);
|
||||
out_unlock:
|
||||
f2fs_unlock_op(sbi);
|
||||
out_new_dir:
|
||||
@ -719,7 +901,7 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
|
||||
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & RENAME_EXCHANGE) {
|
||||
@ -730,53 +912,85 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry,
|
||||
* VFS has already handled the new dentry existence case,
|
||||
* here, we just deal with "RENAME_NOREPLACE" as regular rename.
|
||||
*/
|
||||
return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry);
|
||||
return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
|
||||
}
|
||||
|
||||
static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cookie)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct inode *inode;
|
||||
int err;
|
||||
struct page *cpage = NULL;
|
||||
char *caddr, *paddr = NULL;
|
||||
struct f2fs_str cstr;
|
||||
struct f2fs_str pstr = FSTR_INIT(NULL, 0);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct f2fs_encrypted_symlink_data *sd;
|
||||
loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
|
||||
u32 max_size = inode->i_sb->s_blocksize;
|
||||
int res;
|
||||
|
||||
inode = f2fs_new_inode(dir, mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
res = f2fs_get_encryption_info(inode);
|
||||
if (res)
|
||||
return ERR_PTR(res);
|
||||
|
||||
inode->i_op = &f2fs_file_inode_operations;
|
||||
inode->i_fop = &f2fs_file_operations;
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
cpage = read_mapping_page(inode->i_mapping, 0, NULL);
|
||||
if (IS_ERR(cpage))
|
||||
return ERR_CAST(cpage);
|
||||
caddr = kmap(cpage);
|
||||
caddr[size] = 0;
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = acquire_orphan_inode(sbi);
|
||||
if (err)
|
||||
goto out;
|
||||
/* Symlink is encrypted */
|
||||
sd = (struct f2fs_encrypted_symlink_data *)caddr;
|
||||
cstr.name = sd->encrypted_path;
|
||||
cstr.len = le16_to_cpu(sd->len);
|
||||
|
||||
err = f2fs_do_tmpfile(inode, dir);
|
||||
if (err)
|
||||
goto release_out;
|
||||
/* this is broken symlink case */
|
||||
if (cstr.name[0] == 0 && cstr.len == 0) {
|
||||
res = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/*
|
||||
* add this non-linked tmpfile to orphan list, in this way we could
|
||||
* remove all unused data of tmpfile after abnormal power-off.
|
||||
*/
|
||||
add_orphan_inode(sbi, inode->i_ino);
|
||||
f2fs_unlock_op(sbi);
|
||||
if ((cstr.len + sizeof(struct f2fs_encrypted_symlink_data) - 1) >
|
||||
max_size) {
|
||||
/* Symlink data on the disk is corrupted */
|
||||
res = -EIO;
|
||||
goto errout;
|
||||
}
|
||||
res = f2fs_fname_crypto_alloc_buffer(inode, cstr.len, &pstr);
|
||||
if (res)
|
||||
goto errout;
|
||||
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
res = f2fs_fname_disk_to_usr(inode, NULL, &cstr, &pstr);
|
||||
if (res < 0)
|
||||
goto errout;
|
||||
|
||||
stat_inc_inline_inode(inode);
|
||||
d_tmpfile(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
return 0;
|
||||
paddr = pstr.name;
|
||||
|
||||
release_out:
|
||||
release_orphan_inode(sbi);
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
return err;
|
||||
/* Null-terminate the name */
|
||||
paddr[res] = '\0';
|
||||
|
||||
kunmap(cpage);
|
||||
page_cache_release(cpage);
|
||||
return *cookie = paddr;
|
||||
errout:
|
||||
f2fs_fname_crypto_free_buffer(&pstr);
|
||||
kunmap(cpage);
|
||||
page_cache_release(cpage);
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
|
||||
const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = f2fs_encrypted_follow_link,
|
||||
.put_link = kfree_put_link,
|
||||
.getattr = f2fs_getattr,
|
||||
.setattr = f2fs_setattr,
|
||||
.setxattr = generic_setxattr,
|
||||
.getxattr = generic_getxattr,
|
||||
.listxattr = f2fs_listxattr,
|
||||
.removexattr = generic_removexattr,
|
||||
};
|
||||
#endif
|
||||
|
||||
const struct inode_operations f2fs_dir_inode_operations = {
|
||||
.create = f2fs_create,
|
||||
.lookup = f2fs_lookup,
|
||||
|
@ -195,6 +195,23 @@ static unsigned int __gang_lookup_nat_set(struct f2fs_nm_info *nm_i,
|
||||
start, nr);
|
||||
}
|
||||
|
||||
int need_dentry_mark(struct f2fs_sb_info *sbi, nid_t nid)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct nat_entry *e;
|
||||
bool need = false;
|
||||
|
||||
down_read(&nm_i->nat_tree_lock);
|
||||
e = __lookup_nat_cache(nm_i, nid);
|
||||
if (e) {
|
||||
if (!get_nat_flag(e, IS_CHECKPOINTED) &&
|
||||
!get_nat_flag(e, HAS_FSYNCED_INODE))
|
||||
need = true;
|
||||
}
|
||||
up_read(&nm_i->nat_tree_lock);
|
||||
return need;
|
||||
}
|
||||
|
||||
bool is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
@ -209,20 +226,6 @@ bool is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid)
|
||||
return is_cp;
|
||||
}
|
||||
|
||||
bool has_fsynced_inode(struct f2fs_sb_info *sbi, nid_t ino)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct nat_entry *e;
|
||||
bool fsynced = false;
|
||||
|
||||
down_read(&nm_i->nat_tree_lock);
|
||||
e = __lookup_nat_cache(nm_i, ino);
|
||||
if (e && get_nat_flag(e, HAS_FSYNCED_INODE))
|
||||
fsynced = true;
|
||||
up_read(&nm_i->nat_tree_lock);
|
||||
return fsynced;
|
||||
}
|
||||
|
||||
bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
@ -312,7 +315,8 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
|
||||
__set_nat_cache_dirty(nm_i, e);
|
||||
|
||||
/* update fsync_mark if its inode nat entry is still alive */
|
||||
e = __lookup_nat_cache(nm_i, ni->ino);
|
||||
if (ni->nid != ni->ino)
|
||||
e = __lookup_nat_cache(nm_i, ni->ino);
|
||||
if (e) {
|
||||
if (fsync_done && ni->nid == ni->ino)
|
||||
set_nat_flag(e, HAS_FSYNCED_INODE, true);
|
||||
@ -995,8 +999,11 @@ static int read_node_page(struct page *page, int rw)
|
||||
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
|
||||
struct node_info ni;
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = sbi,
|
||||
.type = NODE,
|
||||
.rw = rw,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
|
||||
get_node_info(sbi, page->index, &ni);
|
||||
@ -1011,7 +1018,7 @@ static int read_node_page(struct page *page, int rw)
|
||||
return LOCKED_PAGE;
|
||||
|
||||
fio.blk_addr = ni.blk_addr;
|
||||
return f2fs_submit_page_bio(sbi, page, &fio);
|
||||
return f2fs_submit_page_bio(&fio);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1204,13 +1211,9 @@ continue_unlock:
|
||||
/* called by fsync() */
|
||||
if (ino && IS_DNODE(page)) {
|
||||
set_fsync_mark(page, 1);
|
||||
if (IS_INODE(page)) {
|
||||
if (!is_checkpointed_node(sbi, ino) &&
|
||||
!has_fsynced_inode(sbi, ino))
|
||||
set_dentry_mark(page, 1);
|
||||
else
|
||||
set_dentry_mark(page, 0);
|
||||
}
|
||||
if (IS_INODE(page))
|
||||
set_dentry_mark(page,
|
||||
need_dentry_mark(sbi, ino));
|
||||
nwritten++;
|
||||
} else {
|
||||
set_fsync_mark(page, 0);
|
||||
@ -1293,8 +1296,11 @@ static int f2fs_write_node_page(struct page *page,
|
||||
nid_t nid;
|
||||
struct node_info ni;
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = sbi,
|
||||
.type = NODE,
|
||||
.rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
|
||||
trace_f2fs_writepage(page, NODE);
|
||||
@ -1329,7 +1335,7 @@ static int f2fs_write_node_page(struct page *page,
|
||||
|
||||
set_page_writeback(page);
|
||||
fio.blk_addr = ni.blk_addr;
|
||||
write_node_page(sbi, page, nid, &fio);
|
||||
write_node_page(nid, &fio);
|
||||
set_node_addr(sbi, &ni, fio.blk_addr, is_fsync_dnode(page));
|
||||
dec_page_count(sbi, F2FS_DIRTY_NODES);
|
||||
up_read(&sbi->node_write);
|
||||
|
@ -343,28 +343,6 @@ static inline nid_t get_nid(struct page *p, int off, bool i)
|
||||
* - Mark cold node blocks in their node footer
|
||||
* - Mark cold data pages in page cache
|
||||
*/
|
||||
static inline int is_file(struct inode *inode, int type)
|
||||
{
|
||||
return F2FS_I(inode)->i_advise & type;
|
||||
}
|
||||
|
||||
static inline void set_file(struct inode *inode, int type)
|
||||
{
|
||||
F2FS_I(inode)->i_advise |= type;
|
||||
}
|
||||
|
||||
static inline void clear_file(struct inode *inode, int type)
|
||||
{
|
||||
F2FS_I(inode)->i_advise &= ~type;
|
||||
}
|
||||
|
||||
#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_set_cold(inode) set_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_lost_pino(inode) set_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_clear_cold(inode) clear_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_got_pino(inode) clear_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
|
||||
static inline int is_cold_data(struct page *page)
|
||||
{
|
||||
return PageChecked(page);
|
||||
|
@ -83,6 +83,11 @@ static int recover_dentry(struct inode *inode, struct page *ipage)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (file_enc_name(inode)) {
|
||||
iput(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
name.len = le32_to_cpu(raw_inode->i_namelen);
|
||||
name.name = raw_inode->i_name;
|
||||
|
||||
@ -143,6 +148,7 @@ out:
|
||||
static void recover_inode(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct f2fs_inode *raw = F2FS_INODE(page);
|
||||
char *name;
|
||||
|
||||
inode->i_mode = le16_to_cpu(raw->i_mode);
|
||||
i_size_write(inode, le64_to_cpu(raw->i_size));
|
||||
@ -153,8 +159,13 @@ static void recover_inode(struct inode *inode, struct page *page)
|
||||
inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec);
|
||||
inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec);
|
||||
|
||||
if (file_enc_name(inode))
|
||||
name = "<encrypted>";
|
||||
else
|
||||
name = F2FS_INODE(page)->i_name;
|
||||
|
||||
f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s",
|
||||
ino_of_node(page), F2FS_INODE(page)->i_name);
|
||||
ino_of_node(page), name);
|
||||
}
|
||||
|
||||
static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
||||
@ -174,7 +185,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
||||
while (1) {
|
||||
struct fsync_inode_entry *entry;
|
||||
|
||||
if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi))
|
||||
if (!is_valid_blkaddr(sbi, blkaddr, META_POR))
|
||||
return 0;
|
||||
|
||||
page = get_meta_page(sbi, blkaddr);
|
||||
@ -349,7 +360,6 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
unsigned int start, end;
|
||||
struct dnode_of_data dn;
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
int err = 0, recovered = 0;
|
||||
|
||||
@ -396,7 +406,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
dest = datablock_addr(page, dn.ofs_in_node);
|
||||
|
||||
if (src != dest && dest != NEW_ADDR && dest != NULL_ADDR &&
|
||||
dest >= MAIN_BLKADDR(sbi) && dest < MAX_BLKADDR(sbi)) {
|
||||
is_valid_blkaddr(sbi, dest, META_POR)) {
|
||||
|
||||
if (src == NULL_ADDR) {
|
||||
err = reserve_new_block(&dn);
|
||||
@ -409,13 +419,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
|
||||
|
||||
/* write dummy data page */
|
||||
recover_data_page(sbi, NULL, &sum, src, dest);
|
||||
dn.data_blkaddr = dest;
|
||||
set_data_blkaddr(&dn);
|
||||
f2fs_update_extent_cache(&dn);
|
||||
f2fs_replace_block(sbi, &dn, src, dest,
|
||||
ni.version, false);
|
||||
recovered++;
|
||||
}
|
||||
dn.ofs_in_node++;
|
||||
@ -454,7 +460,7 @@ static int recover_data(struct f2fs_sb_info *sbi,
|
||||
while (1) {
|
||||
struct fsync_inode_entry *entry;
|
||||
|
||||
if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi))
|
||||
if (!is_valid_blkaddr(sbi, blkaddr, META_POR))
|
||||
break;
|
||||
|
||||
ra_meta_pages_cond(sbi, blkaddr);
|
||||
|
@ -75,6 +75,14 @@ static inline unsigned long __reverse_ffs(unsigned long word)
|
||||
static unsigned long __find_rev_next_bit(const unsigned long *addr,
|
||||
unsigned long size, unsigned long offset)
|
||||
{
|
||||
while (!f2fs_test_bit(offset, (unsigned char *)addr))
|
||||
offset++;
|
||||
|
||||
if (offset > size)
|
||||
offset = size;
|
||||
|
||||
return offset;
|
||||
#if 0
|
||||
const unsigned long *p = addr + BIT_WORD(offset);
|
||||
unsigned long result = offset & ~(BITS_PER_LONG - 1);
|
||||
unsigned long tmp;
|
||||
@ -121,11 +129,20 @@ found_first:
|
||||
return result + size; /* Nope. */
|
||||
found_middle:
|
||||
return result + __reverse_ffs(tmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
static unsigned long __find_rev_next_zero_bit(const unsigned long *addr,
|
||||
unsigned long size, unsigned long offset)
|
||||
{
|
||||
while (f2fs_test_bit(offset, (unsigned char *)addr))
|
||||
offset++;
|
||||
|
||||
if (offset > size)
|
||||
offset = size;
|
||||
|
||||
return offset;
|
||||
#if 0
|
||||
const unsigned long *p = addr + BIT_WORD(offset);
|
||||
unsigned long result = offset & ~(BITS_PER_LONG - 1);
|
||||
unsigned long tmp;
|
||||
@ -173,6 +190,7 @@ found_first:
|
||||
return result + size; /* Nope. */
|
||||
found_middle:
|
||||
return result + __reverse_ffz(tmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void register_inmem_page(struct inode *inode, struct page *page)
|
||||
@ -216,8 +234,10 @@ void commit_inmem_pages(struct inode *inode, bool abort)
|
||||
struct inmem_pages *cur, *tmp;
|
||||
bool submit_bio = false;
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = sbi,
|
||||
.type = DATA,
|
||||
.rw = WRITE_SYNC | REQ_PRIO,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -241,7 +261,8 @@ void commit_inmem_pages(struct inode *inode, bool abort)
|
||||
if (clear_page_dirty_for_io(cur->page))
|
||||
inode_dec_dirty_pages(inode);
|
||||
trace_f2fs_commit_inmem_page(cur->page, INMEM);
|
||||
do_write_data_page(cur->page, &fio);
|
||||
fio.page = cur->page;
|
||||
do_write_data_page(&fio);
|
||||
submit_bio = true;
|
||||
}
|
||||
f2fs_put_page(cur->page, 1);
|
||||
@ -466,22 +487,43 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi,
|
||||
{
|
||||
sector_t start = SECTOR_FROM_BLOCK(blkstart);
|
||||
sector_t len = SECTOR_FROM_BLOCK(blklen);
|
||||
struct seg_entry *se;
|
||||
unsigned int offset;
|
||||
block_t i;
|
||||
|
||||
for (i = blkstart; i < blkstart + blklen; i++) {
|
||||
se = get_seg_entry(sbi, GET_SEGNO(sbi, i));
|
||||
offset = GET_BLKOFF_FROM_SEG0(sbi, i);
|
||||
|
||||
if (!f2fs_test_and_set_bit(offset, se->discard_map))
|
||||
sbi->discard_blks--;
|
||||
}
|
||||
trace_f2fs_issue_discard(sbi->sb, blkstart, blklen);
|
||||
return blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0);
|
||||
}
|
||||
|
||||
void discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr)
|
||||
{
|
||||
if (f2fs_issue_discard(sbi, blkaddr, 1)) {
|
||||
struct page *page = grab_meta_page(sbi, blkaddr);
|
||||
/* zero-filled page */
|
||||
set_page_dirty(page);
|
||||
f2fs_put_page(page, 1);
|
||||
int err = -ENOTSUPP;
|
||||
|
||||
if (test_opt(sbi, DISCARD)) {
|
||||
struct seg_entry *se = get_seg_entry(sbi,
|
||||
GET_SEGNO(sbi, blkaddr));
|
||||
unsigned int offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr);
|
||||
|
||||
if (f2fs_test_bit(offset, se->discard_map))
|
||||
return;
|
||||
|
||||
err = f2fs_issue_discard(sbi, blkaddr, 1);
|
||||
}
|
||||
|
||||
if (err)
|
||||
update_meta_page(sbi, NULL, blkaddr);
|
||||
}
|
||||
|
||||
static void __add_discard_entry(struct f2fs_sb_info *sbi,
|
||||
struct cp_control *cpc, unsigned int start, unsigned int end)
|
||||
struct cp_control *cpc, struct seg_entry *se,
|
||||
unsigned int start, unsigned int end)
|
||||
{
|
||||
struct list_head *head = &SM_I(sbi)->discard_list;
|
||||
struct discard_entry *new, *last;
|
||||
@ -502,7 +544,6 @@ static void __add_discard_entry(struct f2fs_sb_info *sbi,
|
||||
list_add_tail(&new->list, head);
|
||||
done:
|
||||
SM_I(sbi)->nr_discards += end - start;
|
||||
cpc->trimmed += end - start;
|
||||
}
|
||||
|
||||
static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
@ -512,41 +553,24 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start);
|
||||
unsigned long *cur_map = (unsigned long *)se->cur_valid_map;
|
||||
unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map;
|
||||
unsigned long *discard_map = (unsigned long *)se->discard_map;
|
||||
unsigned long *dmap = SIT_I(sbi)->tmp_map;
|
||||
unsigned int start = 0, end = -1;
|
||||
bool force = (cpc->reason == CP_DISCARD);
|
||||
int i;
|
||||
|
||||
if (!force && (!test_opt(sbi, DISCARD) ||
|
||||
SM_I(sbi)->nr_discards >= SM_I(sbi)->max_discards))
|
||||
if (se->valid_blocks == max_blocks)
|
||||
return;
|
||||
|
||||
if (force && !se->valid_blocks) {
|
||||
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
|
||||
/*
|
||||
* if this segment is registered in the prefree list, then
|
||||
* we should skip adding a discard candidate, and let the
|
||||
* checkpoint do that later.
|
||||
*/
|
||||
mutex_lock(&dirty_i->seglist_lock);
|
||||
if (test_bit(cpc->trim_start, dirty_i->dirty_segmap[PRE])) {
|
||||
mutex_unlock(&dirty_i->seglist_lock);
|
||||
cpc->trimmed += sbi->blocks_per_seg;
|
||||
if (!force) {
|
||||
if (!test_opt(sbi, DISCARD) || !se->valid_blocks ||
|
||||
SM_I(sbi)->nr_discards >= SM_I(sbi)->max_discards)
|
||||
return;
|
||||
}
|
||||
mutex_unlock(&dirty_i->seglist_lock);
|
||||
|
||||
__add_discard_entry(sbi, cpc, 0, sbi->blocks_per_seg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* zero block will be discarded through the prefree list */
|
||||
if (!se->valid_blocks || se->valid_blocks == max_blocks)
|
||||
return;
|
||||
|
||||
/* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */
|
||||
for (i = 0; i < entries; i++)
|
||||
dmap[i] = force ? ~ckpt_map[i] :
|
||||
dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] :
|
||||
(cur_map[i] ^ ckpt_map[i]) & ckpt_map[i];
|
||||
|
||||
while (force || SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) {
|
||||
@ -555,11 +579,7 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
break;
|
||||
|
||||
end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1);
|
||||
|
||||
if (force && end - start < cpc->trim_minlen)
|
||||
continue;
|
||||
|
||||
__add_discard_entry(sbi, cpc, start, end);
|
||||
__add_discard_entry(sbi, cpc, se, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
@ -589,7 +609,7 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi)
|
||||
mutex_unlock(&dirty_i->seglist_lock);
|
||||
}
|
||||
|
||||
void clear_prefree_segments(struct f2fs_sb_info *sbi)
|
||||
void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
{
|
||||
struct list_head *head = &(SM_I(sbi)->discard_list);
|
||||
struct discard_entry *entry, *this;
|
||||
@ -622,7 +642,11 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi)
|
||||
|
||||
/* send small discards */
|
||||
list_for_each_entry_safe(entry, this, head, list) {
|
||||
if (cpc->reason == CP_DISCARD && entry->len < cpc->trim_minlen)
|
||||
goto skip;
|
||||
f2fs_issue_discard(sbi, entry->blkaddr, entry->len);
|
||||
cpc->trimmed += entry->len;
|
||||
skip:
|
||||
list_del(&entry->list);
|
||||
SM_I(sbi)->nr_discards -= entry->len;
|
||||
kmem_cache_free(discard_entry_slab, entry);
|
||||
@ -673,9 +697,13 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
|
||||
if (del > 0) {
|
||||
if (f2fs_test_and_set_bit(offset, se->cur_valid_map))
|
||||
f2fs_bug_on(sbi, 1);
|
||||
if (!f2fs_test_and_set_bit(offset, se->discard_map))
|
||||
sbi->discard_blks--;
|
||||
} else {
|
||||
if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map))
|
||||
f2fs_bug_on(sbi, 1);
|
||||
if (f2fs_test_and_clear_bit(offset, se->discard_map))
|
||||
sbi->discard_blks++;
|
||||
}
|
||||
if (!f2fs_test_bit(offset, se->ckpt_valid_map))
|
||||
se->ckpt_valid_blocks += del;
|
||||
@ -769,14 +797,23 @@ struct page *get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
return get_meta_page(sbi, GET_SUM_BLOCK(sbi, segno));
|
||||
}
|
||||
|
||||
void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr)
|
||||
{
|
||||
struct page *page = grab_meta_page(sbi, blk_addr);
|
||||
void *dst = page_address(page);
|
||||
|
||||
if (src)
|
||||
memcpy(dst, src, PAGE_CACHE_SIZE);
|
||||
else
|
||||
memset(dst, 0, PAGE_CACHE_SIZE);
|
||||
set_page_dirty(page);
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
static void write_sum_page(struct f2fs_sb_info *sbi,
|
||||
struct f2fs_summary_block *sum_blk, block_t blk_addr)
|
||||
{
|
||||
struct page *page = grab_meta_page(sbi, blk_addr);
|
||||
void *kaddr = page_address(page);
|
||||
memcpy(kaddr, sum_blk, PAGE_CACHE_SIZE);
|
||||
set_page_dirty(page);
|
||||
f2fs_put_page(page, 1);
|
||||
update_meta_page(sbi, (void *)sum_blk, blk_addr);
|
||||
}
|
||||
|
||||
static int is_next_segment_free(struct f2fs_sb_info *sbi, int type)
|
||||
@ -1060,8 +1097,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
|
||||
unsigned int start_segno, end_segno;
|
||||
struct cp_control cpc;
|
||||
|
||||
if (range->minlen > SEGMENT_SIZE(sbi) || start >= MAX_BLKADDR(sbi) ||
|
||||
range->len < sbi->blocksize)
|
||||
if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize)
|
||||
return -EINVAL;
|
||||
|
||||
cpc.trimmed = 0;
|
||||
@ -1073,12 +1109,19 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
|
||||
end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 :
|
||||
GET_SEGNO(sbi, end);
|
||||
cpc.reason = CP_DISCARD;
|
||||
cpc.trim_minlen = F2FS_BYTES_TO_BLK(range->minlen);
|
||||
cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen));
|
||||
|
||||
/* do checkpoint to issue discard commands safely */
|
||||
for (; start_segno <= end_segno; start_segno = cpc.trim_end + 1) {
|
||||
cpc.trim_start = start_segno;
|
||||
cpc.trim_end = min_t(unsigned int, rounddown(start_segno +
|
||||
|
||||
if (sbi->discard_blks == 0)
|
||||
break;
|
||||
else if (sbi->discard_blks < BATCHED_TRIM_BLOCKS(sbi))
|
||||
cpc.trim_end = end_segno;
|
||||
else
|
||||
cpc.trim_end = min_t(unsigned int,
|
||||
rounddown(start_segno +
|
||||
BATCHED_TRIM_SEGMENTS(sbi),
|
||||
sbi->segs_per_sec) - 1, end_segno);
|
||||
|
||||
@ -1206,84 +1249,95 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
|
||||
mutex_unlock(&curseg->curseg_mutex);
|
||||
}
|
||||
|
||||
static void do_write_page(struct f2fs_sb_info *sbi, struct page *page,
|
||||
struct f2fs_summary *sum,
|
||||
struct f2fs_io_info *fio)
|
||||
static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio)
|
||||
{
|
||||
int type = __get_segment_type(page, fio->type);
|
||||
int type = __get_segment_type(fio->page, fio->type);
|
||||
|
||||
allocate_data_block(sbi, page, fio->blk_addr, &fio->blk_addr, sum, type);
|
||||
allocate_data_block(fio->sbi, fio->page, fio->blk_addr,
|
||||
&fio->blk_addr, sum, type);
|
||||
|
||||
/* writeout dirty page into bdev */
|
||||
f2fs_submit_page_mbio(sbi, page, fio);
|
||||
f2fs_submit_page_mbio(fio);
|
||||
}
|
||||
|
||||
void write_meta_page(struct f2fs_sb_info *sbi, struct page *page)
|
||||
{
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = sbi,
|
||||
.type = META,
|
||||
.rw = WRITE_SYNC | REQ_META | REQ_PRIO,
|
||||
.blk_addr = page->index,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
|
||||
set_page_writeback(page);
|
||||
f2fs_submit_page_mbio(sbi, page, &fio);
|
||||
f2fs_submit_page_mbio(&fio);
|
||||
}
|
||||
|
||||
void write_node_page(struct f2fs_sb_info *sbi, struct page *page,
|
||||
unsigned int nid, struct f2fs_io_info *fio)
|
||||
void write_node_page(unsigned int nid, struct f2fs_io_info *fio)
|
||||
{
|
||||
struct f2fs_summary sum;
|
||||
|
||||
set_summary(&sum, nid, 0, 0);
|
||||
do_write_page(sbi, page, &sum, fio);
|
||||
do_write_page(&sum, fio);
|
||||
}
|
||||
|
||||
void write_data_page(struct page *page, struct dnode_of_data *dn,
|
||||
struct f2fs_io_info *fio)
|
||||
void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
|
||||
struct f2fs_sb_info *sbi = fio->sbi;
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
|
||||
f2fs_bug_on(sbi, dn->data_blkaddr == NULL_ADDR);
|
||||
get_node_info(sbi, dn->nid, &ni);
|
||||
set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
|
||||
do_write_page(sbi, page, &sum, fio);
|
||||
do_write_page(&sum, fio);
|
||||
dn->data_blkaddr = fio->blk_addr;
|
||||
}
|
||||
|
||||
void rewrite_data_page(struct page *page, struct f2fs_io_info *fio)
|
||||
void rewrite_data_page(struct f2fs_io_info *fio)
|
||||
{
|
||||
stat_inc_inplace_blocks(F2FS_P_SB(page));
|
||||
f2fs_submit_page_mbio(F2FS_P_SB(page), page, fio);
|
||||
stat_inc_inplace_blocks(fio->sbi);
|
||||
f2fs_submit_page_mbio(fio);
|
||||
}
|
||||
|
||||
void recover_data_page(struct f2fs_sb_info *sbi,
|
||||
struct page *page, struct f2fs_summary *sum,
|
||||
block_t old_blkaddr, block_t new_blkaddr)
|
||||
static void __f2fs_replace_block(struct f2fs_sb_info *sbi,
|
||||
struct f2fs_summary *sum,
|
||||
block_t old_blkaddr, block_t new_blkaddr,
|
||||
bool recover_curseg)
|
||||
{
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
struct curseg_info *curseg;
|
||||
unsigned int segno, old_cursegno;
|
||||
struct seg_entry *se;
|
||||
int type;
|
||||
unsigned short old_blkoff;
|
||||
|
||||
segno = GET_SEGNO(sbi, new_blkaddr);
|
||||
se = get_seg_entry(sbi, segno);
|
||||
type = se->type;
|
||||
|
||||
if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) {
|
||||
if (old_blkaddr == NULL_ADDR)
|
||||
type = CURSEG_COLD_DATA;
|
||||
else
|
||||
if (!recover_curseg) {
|
||||
/* for recovery flow */
|
||||
if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) {
|
||||
if (old_blkaddr == NULL_ADDR)
|
||||
type = CURSEG_COLD_DATA;
|
||||
else
|
||||
type = CURSEG_WARM_DATA;
|
||||
}
|
||||
} else {
|
||||
if (!IS_CURSEG(sbi, segno))
|
||||
type = CURSEG_WARM_DATA;
|
||||
}
|
||||
|
||||
curseg = CURSEG_I(sbi, type);
|
||||
|
||||
mutex_lock(&curseg->curseg_mutex);
|
||||
mutex_lock(&sit_i->sentry_lock);
|
||||
|
||||
old_cursegno = curseg->segno;
|
||||
old_blkoff = curseg->next_blkoff;
|
||||
|
||||
/* change the current segment */
|
||||
if (segno != curseg->segno) {
|
||||
@ -1297,30 +1351,67 @@ void recover_data_page(struct f2fs_sb_info *sbi,
|
||||
refresh_sit_entry(sbi, old_blkaddr, new_blkaddr);
|
||||
locate_dirty_segment(sbi, old_cursegno);
|
||||
|
||||
if (recover_curseg) {
|
||||
if (old_cursegno != curseg->segno) {
|
||||
curseg->next_segno = old_cursegno;
|
||||
change_curseg(sbi, type, true);
|
||||
}
|
||||
curseg->next_blkoff = old_blkoff;
|
||||
}
|
||||
|
||||
mutex_unlock(&sit_i->sentry_lock);
|
||||
mutex_unlock(&curseg->curseg_mutex);
|
||||
}
|
||||
|
||||
void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
|
||||
block_t old_addr, block_t new_addr,
|
||||
unsigned char version, bool recover_curseg)
|
||||
{
|
||||
struct f2fs_summary sum;
|
||||
|
||||
set_summary(&sum, dn->nid, dn->ofs_in_node, version);
|
||||
|
||||
__f2fs_replace_block(sbi, &sum, old_addr, new_addr, recover_curseg);
|
||||
|
||||
dn->data_blkaddr = new_addr;
|
||||
set_data_blkaddr(dn);
|
||||
f2fs_update_extent_cache(dn);
|
||||
}
|
||||
|
||||
static inline bool is_merged_page(struct f2fs_sb_info *sbi,
|
||||
struct page *page, enum page_type type)
|
||||
{
|
||||
enum page_type btype = PAGE_TYPE_OF_BIO(type);
|
||||
struct f2fs_bio_info *io = &sbi->write_io[btype];
|
||||
struct bio_vec *bvec;
|
||||
struct page *target;
|
||||
int i;
|
||||
|
||||
down_read(&io->io_rwsem);
|
||||
if (!io->bio)
|
||||
goto out;
|
||||
if (!io->bio) {
|
||||
up_read(&io->io_rwsem);
|
||||
return false;
|
||||
}
|
||||
|
||||
bio_for_each_segment_all(bvec, io->bio, i) {
|
||||
if (page == bvec->bv_page) {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
up_read(&io->io_rwsem);
|
||||
return false;
|
||||
}
|
||||
@ -1857,8 +1948,11 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
|
||||
= kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL);
|
||||
sit_i->sentries[start].ckpt_valid_map
|
||||
= kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL);
|
||||
if (!sit_i->sentries[start].cur_valid_map
|
||||
|| !sit_i->sentries[start].ckpt_valid_map)
|
||||
sit_i->sentries[start].discard_map
|
||||
= kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL);
|
||||
if (!sit_i->sentries[start].cur_valid_map ||
|
||||
!sit_i->sentries[start].ckpt_valid_map ||
|
||||
!sit_i->sentries[start].discard_map)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -1996,6 +2090,11 @@ static void build_sit_entries(struct f2fs_sb_info *sbi)
|
||||
got_it:
|
||||
check_block_count(sbi, start, &sit);
|
||||
seg_info_from_raw_sit(se, &sit);
|
||||
|
||||
/* build discard map only one time */
|
||||
memcpy(se->discard_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE);
|
||||
sbi->discard_blks += sbi->blocks_per_seg - se->valid_blocks;
|
||||
|
||||
if (sbi->segs_per_sec > 1) {
|
||||
struct sec_entry *e = get_sec_entry(sbi, start);
|
||||
e->valid_blocks += se->valid_blocks;
|
||||
@ -2245,6 +2344,7 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi)
|
||||
for (start = 0; start < MAIN_SEGS(sbi); start++) {
|
||||
kfree(sit_i->sentries[start].cur_valid_map);
|
||||
kfree(sit_i->sentries[start].ckpt_valid_map);
|
||||
kfree(sit_i->sentries[start].discard_map);
|
||||
}
|
||||
}
|
||||
kfree(sit_i->tmp_map);
|
||||
|
@ -163,6 +163,7 @@ struct seg_entry {
|
||||
*/
|
||||
unsigned short ckpt_valid_blocks;
|
||||
unsigned char *ckpt_valid_map;
|
||||
unsigned char *discard_map;
|
||||
unsigned char type; /* segment type like CURSEG_XXX_TYPE */
|
||||
unsigned long long mtime; /* modification time of the segment */
|
||||
};
|
||||
|
178
fs/f2fs/super.c
178
fs/f2fs/super.c
@ -258,6 +258,7 @@ static void init_once(void *foo)
|
||||
static int parse_options(struct super_block *sb, char *options)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||
struct request_queue *q;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
char *p, *name;
|
||||
int arg = 0;
|
||||
@ -302,7 +303,14 @@ static int parse_options(struct super_block *sb, char *options)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case Opt_discard:
|
||||
set_opt(sbi, DISCARD);
|
||||
q = bdev_get_queue(sb->s_bdev);
|
||||
if (blk_queue_discard(q)) {
|
||||
set_opt(sbi, DISCARD);
|
||||
} else {
|
||||
f2fs_msg(sb, KERN_WARNING,
|
||||
"mounting with \"discard\" option, but "
|
||||
"the device does not support discard");
|
||||
}
|
||||
break;
|
||||
case Opt_noheap:
|
||||
set_opt(sbi, NOHEAP);
|
||||
@ -416,6 +424,9 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
|
||||
/* Will be used by directory only */
|
||||
fi->i_dir_level = F2FS_SB(sb)->dir_level;
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
fi->i_crypt_info = NULL;
|
||||
#endif
|
||||
return &fi->vfs_inode;
|
||||
}
|
||||
|
||||
@ -428,8 +439,31 @@ static int f2fs_drop_inode(struct inode *inode)
|
||||
* - f2fs_gc -> iput -> evict
|
||||
* - inode_wait_for_writeback(inode)
|
||||
*/
|
||||
if (!inode_unhashed(inode) && inode->i_state & I_SYNC)
|
||||
if (!inode_unhashed(inode) && inode->i_state & I_SYNC) {
|
||||
if (!inode->i_nlink && !is_bad_inode(inode)) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
/* some remained atomic pages should discarded */
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
commit_inmem_pages(inode, true);
|
||||
|
||||
sb_start_intwrite(inode->i_sb);
|
||||
i_size_write(inode, 0);
|
||||
|
||||
if (F2FS_HAS_BLOCKS(inode))
|
||||
f2fs_truncate(inode);
|
||||
|
||||
sb_end_intwrite(inode->i_sb);
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_ENCRYPTION
|
||||
if (F2FS_I(inode)->i_crypt_info)
|
||||
f2fs_free_encryption_info(inode,
|
||||
F2FS_I(inode)->i_crypt_info);
|
||||
#endif
|
||||
spin_lock(&inode->i_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return generic_drop_inode(inode);
|
||||
}
|
||||
|
||||
@ -520,7 +554,7 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
|
||||
} else {
|
||||
f2fs_balance_fs(sbi);
|
||||
}
|
||||
f2fs_trace_ios(NULL, NULL, 1);
|
||||
f2fs_trace_ios(NULL, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -658,6 +692,22 @@ static const struct file_operations f2fs_seq_segment_info_fops = {
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void default_options(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
/* init some FS parameters */
|
||||
sbi->active_logs = NR_CURSEG_TYPE;
|
||||
|
||||
set_opt(sbi, BG_GC);
|
||||
set_opt(sbi, INLINE_DATA);
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_XATTR
|
||||
set_opt(sbi, XATTR_USER);
|
||||
#endif
|
||||
#ifdef CONFIG_F2FS_FS_POSIX_ACL
|
||||
set_opt(sbi, POSIX_ACL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||
@ -676,7 +726,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
||||
active_logs = sbi->active_logs;
|
||||
|
||||
sbi->mount_opt.opt = 0;
|
||||
sbi->active_logs = NR_CURSEG_TYPE;
|
||||
default_options(sbi);
|
||||
|
||||
/* parse mount options */
|
||||
err = parse_options(sb, data);
|
||||
@ -929,29 +979,36 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
|
||||
*/
|
||||
static int read_raw_super_block(struct super_block *sb,
|
||||
struct f2fs_super_block **raw_super,
|
||||
struct buffer_head **raw_super_buf)
|
||||
struct buffer_head **raw_super_buf,
|
||||
int *recovery)
|
||||
{
|
||||
int block = 0;
|
||||
struct buffer_head *buffer;
|
||||
struct f2fs_super_block *super;
|
||||
int err = 0;
|
||||
|
||||
retry:
|
||||
*raw_super_buf = sb_bread(sb, block);
|
||||
if (!*raw_super_buf) {
|
||||
buffer = sb_bread(sb, block);
|
||||
if (!buffer) {
|
||||
*recovery = 1;
|
||||
f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock",
|
||||
block + 1);
|
||||
if (block == 0) {
|
||||
block++;
|
||||
goto retry;
|
||||
} else {
|
||||
return -EIO;
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
*raw_super = (struct f2fs_super_block *)
|
||||
((char *)(*raw_super_buf)->b_data + F2FS_SUPER_OFFSET);
|
||||
super = (struct f2fs_super_block *)
|
||||
((char *)(buffer)->b_data + F2FS_SUPER_OFFSET);
|
||||
|
||||
/* sanity checking of raw super */
|
||||
if (sanity_check_raw_super(sb, *raw_super)) {
|
||||
brelse(*raw_super_buf);
|
||||
if (sanity_check_raw_super(sb, super)) {
|
||||
brelse(buffer);
|
||||
*recovery = 1;
|
||||
f2fs_msg(sb, KERN_ERR,
|
||||
"Can't find valid F2FS filesystem in %dth superblock",
|
||||
block + 1);
|
||||
@ -959,25 +1016,76 @@ retry:
|
||||
block++;
|
||||
goto retry;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*raw_super) {
|
||||
*raw_super_buf = buffer;
|
||||
*raw_super = super;
|
||||
} else {
|
||||
/* already have a valid superblock */
|
||||
brelse(buffer);
|
||||
}
|
||||
|
||||
/* check the validity of the second superblock */
|
||||
if (block == 0) {
|
||||
block++;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
out:
|
||||
/* No valid superblock */
|
||||
if (!*raw_super)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover)
|
||||
{
|
||||
struct buffer_head *sbh = sbi->raw_super_buf;
|
||||
sector_t block = sbh->b_blocknr;
|
||||
int err;
|
||||
|
||||
/* write back-up superblock first */
|
||||
sbh->b_blocknr = block ? 0 : 1;
|
||||
mark_buffer_dirty(sbh);
|
||||
err = sync_dirty_buffer(sbh);
|
||||
|
||||
sbh->b_blocknr = block;
|
||||
|
||||
/* if we are in recovery path, skip writing valid superblock */
|
||||
if (recover || err)
|
||||
goto out;
|
||||
|
||||
/* write current valid superblock */
|
||||
mark_buffer_dirty(sbh);
|
||||
err = sync_dirty_buffer(sbh);
|
||||
out:
|
||||
clear_buffer_write_io_error(sbh);
|
||||
set_buffer_uptodate(sbh);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct f2fs_sb_info *sbi;
|
||||
struct f2fs_super_block *raw_super = NULL;
|
||||
struct f2fs_super_block *raw_super;
|
||||
struct buffer_head *raw_super_buf;
|
||||
struct inode *root;
|
||||
long err = -EINVAL;
|
||||
long err;
|
||||
bool retry = true, need_fsck = false;
|
||||
char *options = NULL;
|
||||
int i;
|
||||
int recovery, i;
|
||||
|
||||
try_onemore:
|
||||
err = -EINVAL;
|
||||
raw_super = NULL;
|
||||
raw_super_buf = NULL;
|
||||
recovery = 0;
|
||||
|
||||
/* allocate memory for f2fs-specific super block info */
|
||||
sbi = kzalloc(sizeof(struct f2fs_sb_info), GFP_KERNEL);
|
||||
if (!sbi)
|
||||
@ -989,23 +1097,12 @@ try_onemore:
|
||||
goto free_sbi;
|
||||
}
|
||||
|
||||
err = read_raw_super_block(sb, &raw_super, &raw_super_buf);
|
||||
err = read_raw_super_block(sb, &raw_super, &raw_super_buf, &recovery);
|
||||
if (err)
|
||||
goto free_sbi;
|
||||
|
||||
sb->s_fs_info = sbi;
|
||||
/* init some FS parameters */
|
||||
sbi->active_logs = NR_CURSEG_TYPE;
|
||||
|
||||
set_opt(sbi, BG_GC);
|
||||
set_opt(sbi, INLINE_DATA);
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_XATTR
|
||||
set_opt(sbi, XATTR_USER);
|
||||
#endif
|
||||
#ifdef CONFIG_F2FS_FS_POSIX_ACL
|
||||
set_opt(sbi, POSIX_ACL);
|
||||
#endif
|
||||
default_options(sbi);
|
||||
/* parse mount options */
|
||||
options = kstrdup((const char *)data, GFP_KERNEL);
|
||||
if (data && !options) {
|
||||
@ -1148,14 +1245,6 @@ try_onemore:
|
||||
proc_create_data("segment_info", S_IRUGO, sbi->s_proc,
|
||||
&f2fs_seq_segment_info_fops, sb);
|
||||
|
||||
if (test_opt(sbi, DISCARD)) {
|
||||
struct request_queue *q = bdev_get_queue(sb->s_bdev);
|
||||
if (!blk_queue_discard(q))
|
||||
f2fs_msg(sb, KERN_WARNING,
|
||||
"mounting with \"discard\" option, but "
|
||||
"the device does not support discard");
|
||||
}
|
||||
|
||||
sbi->s_kobj.kset = f2fs_kset;
|
||||
init_completion(&sbi->s_kobj_unregister);
|
||||
err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL,
|
||||
@ -1198,6 +1287,13 @@ try_onemore:
|
||||
goto free_kobj;
|
||||
}
|
||||
kfree(options);
|
||||
|
||||
/* recover broken superblock */
|
||||
if (recovery && !f2fs_readonly(sb) && !bdev_read_only(sb->s_bdev)) {
|
||||
f2fs_msg(sb, KERN_INFO, "Recover invalid superblock");
|
||||
f2fs_commit_super(sbi, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_kobj:
|
||||
@ -1305,13 +1401,18 @@ static int __init init_f2fs_fs(void)
|
||||
err = -ENOMEM;
|
||||
goto free_extent_cache;
|
||||
}
|
||||
err = register_filesystem(&f2fs_fs_type);
|
||||
err = f2fs_init_crypto();
|
||||
if (err)
|
||||
goto free_kset;
|
||||
err = register_filesystem(&f2fs_fs_type);
|
||||
if (err)
|
||||
goto free_crypto;
|
||||
f2fs_create_root_stats();
|
||||
f2fs_proc_root = proc_mkdir("fs/f2fs", NULL);
|
||||
return 0;
|
||||
|
||||
free_crypto:
|
||||
f2fs_exit_crypto();
|
||||
free_kset:
|
||||
kset_unregister(f2fs_kset);
|
||||
free_extent_cache:
|
||||
@ -1333,6 +1434,7 @@ static void __exit exit_f2fs_fs(void)
|
||||
remove_proc_entry("fs/f2fs", NULL);
|
||||
f2fs_destroy_root_stats();
|
||||
unregister_filesystem(&f2fs_fs_type);
|
||||
f2fs_exit_crypto();
|
||||
destroy_extent_cache();
|
||||
destroy_checkpoint_caches();
|
||||
destroy_segment_manager_caches();
|
||||
|
@ -80,7 +80,7 @@ out:
|
||||
radix_tree_preload_end();
|
||||
}
|
||||
|
||||
void f2fs_trace_ios(struct page *page, struct f2fs_io_info *fio, int flush)
|
||||
void f2fs_trace_ios(struct f2fs_io_info *fio, int flush)
|
||||
{
|
||||
struct inode *inode;
|
||||
pid_t pid;
|
||||
@ -91,8 +91,8 @@ void f2fs_trace_ios(struct page *page, struct f2fs_io_info *fio, int flush)
|
||||
return;
|
||||
}
|
||||
|
||||
inode = page->mapping->host;
|
||||
pid = page_private(page);
|
||||
inode = fio->page->mapping->host;
|
||||
pid = page_private(fio->page);
|
||||
|
||||
major = MAJOR(inode->i_sb->s_dev);
|
||||
minor = MINOR(inode->i_sb->s_dev);
|
||||
|
@ -33,12 +33,12 @@ struct last_io_info {
|
||||
};
|
||||
|
||||
extern void f2fs_trace_pid(struct page *);
|
||||
extern void f2fs_trace_ios(struct page *, struct f2fs_io_info *, int);
|
||||
extern void f2fs_trace_ios(struct f2fs_io_info *, int);
|
||||
extern void f2fs_build_trace_ios(void);
|
||||
extern void f2fs_destroy_trace_ios(void);
|
||||
#else
|
||||
#define f2fs_trace_pid(p)
|
||||
#define f2fs_trace_ios(p, i, n)
|
||||
#define f2fs_trace_ios(i, n)
|
||||
#define f2fs_build_trace_ios()
|
||||
#define f2fs_destroy_trace_ios()
|
||||
|
||||
|
@ -584,6 +584,9 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
||||
inode->i_ctime = CURRENT_TIME;
|
||||
clear_inode_flag(fi, FI_ACL_MODE);
|
||||
}
|
||||
if (index == F2FS_XATTR_INDEX_ENCRYPTION &&
|
||||
!strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT))
|
||||
f2fs_set_encrypted_inode(inode);
|
||||
|
||||
if (ipage)
|
||||
update_inode(inode, ipage);
|
||||
|
@ -35,6 +35,10 @@
|
||||
#define F2FS_XATTR_INDEX_LUSTRE 5
|
||||
#define F2FS_XATTR_INDEX_SECURITY 6
|
||||
#define F2FS_XATTR_INDEX_ADVISE 7
|
||||
/* Should be same as EXT4_XATTR_INDEX_ENCRYPTION */
|
||||
#define F2FS_XATTR_INDEX_ENCRYPTION 9
|
||||
|
||||
#define F2FS_XATTR_NAME_ENCRYPTION_CONTEXT "c"
|
||||
|
||||
struct f2fs_xattr_header {
|
||||
__le32 h_magic; /* magic number for identification */
|
||||
|
@ -50,6 +50,8 @@
|
||||
#define MAX_ACTIVE_NODE_LOGS 8
|
||||
#define MAX_ACTIVE_DATA_LOGS 8
|
||||
|
||||
#define VERSION_LEN 256
|
||||
|
||||
/*
|
||||
* For superblock
|
||||
*/
|
||||
@ -86,6 +88,12 @@ struct f2fs_super_block {
|
||||
__le32 extension_count; /* # of extensions below */
|
||||
__u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */
|
||||
__le32 cp_payload;
|
||||
__u8 version[VERSION_LEN]; /* the kernel version */
|
||||
__u8 init_version[VERSION_LEN]; /* the initial kernel version */
|
||||
__le32 feature; /* defined features */
|
||||
__u8 encryption_level; /* versioning level for encryption */
|
||||
__u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
|
||||
__u8 reserved[871]; /* valid reserved region */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
|
@ -13,6 +13,10 @@ TRACE_DEFINE_ENUM(NODE);
|
||||
TRACE_DEFINE_ENUM(DATA);
|
||||
TRACE_DEFINE_ENUM(META);
|
||||
TRACE_DEFINE_ENUM(META_FLUSH);
|
||||
TRACE_DEFINE_ENUM(INMEM);
|
||||
TRACE_DEFINE_ENUM(INMEM_DROP);
|
||||
TRACE_DEFINE_ENUM(IPU);
|
||||
TRACE_DEFINE_ENUM(OPU);
|
||||
TRACE_DEFINE_ENUM(CURSEG_HOT_DATA);
|
||||
TRACE_DEFINE_ENUM(CURSEG_WARM_DATA);
|
||||
TRACE_DEFINE_ENUM(CURSEG_COLD_DATA);
|
||||
@ -37,6 +41,7 @@ TRACE_DEFINE_ENUM(__REQ_META);
|
||||
TRACE_DEFINE_ENUM(CP_UMOUNT);
|
||||
TRACE_DEFINE_ENUM(CP_FASTBOOT);
|
||||
TRACE_DEFINE_ENUM(CP_SYNC);
|
||||
TRACE_DEFINE_ENUM(CP_RECOVERY);
|
||||
TRACE_DEFINE_ENUM(CP_DISCARD);
|
||||
|
||||
#define show_block_type(type) \
|
||||
@ -112,6 +117,7 @@ TRACE_DEFINE_ENUM(CP_DISCARD);
|
||||
{ CP_DISCARD, "Discard" })
|
||||
|
||||
struct victim_sel_policy;
|
||||
struct f2fs_map_blocks;
|
||||
|
||||
DECLARE_EVENT_CLASS(f2fs__inode,
|
||||
|
||||
@ -476,36 +482,35 @@ TRACE_EVENT(f2fs_truncate_partial_nodes,
|
||||
__entry->err)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_get_data_block,
|
||||
TP_PROTO(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh, int ret),
|
||||
TRACE_EVENT(f2fs_map_blocks,
|
||||
TP_PROTO(struct inode *inode, struct f2fs_map_blocks *map, int ret),
|
||||
|
||||
TP_ARGS(inode, iblock, bh, ret),
|
||||
TP_ARGS(inode, map, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(sector_t, iblock)
|
||||
__field(sector_t, bh_start)
|
||||
__field(size_t, bh_size)
|
||||
__field(block_t, m_lblk)
|
||||
__field(block_t, m_pblk)
|
||||
__field(unsigned int, m_len)
|
||||
__field(int, ret)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->iblock = iblock;
|
||||
__entry->bh_start = bh->b_blocknr;
|
||||
__entry->bh_size = bh->b_size;
|
||||
__entry->m_lblk = map->m_lblk;
|
||||
__entry->m_pblk = map->m_pblk;
|
||||
__entry->m_len = map->m_len;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, file offset = %llu, "
|
||||
"start blkaddr = 0x%llx, len = 0x%llx bytes, err = %d",
|
||||
"start blkaddr = 0x%llx, len = 0x%llx, err = %d",
|
||||
show_dev_ino(__entry),
|
||||
(unsigned long long)__entry->iblock,
|
||||
(unsigned long long)__entry->bh_start,
|
||||
(unsigned long long)__entry->bh_size,
|
||||
(unsigned long long)__entry->m_lblk,
|
||||
(unsigned long long)__entry->m_pblk,
|
||||
(unsigned long long)__entry->m_len,
|
||||
__entry->ret)
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user