eCryptfs: fixes issue where files sometimes got corrupted upon close
Fixes implementation of cache cleaning, it was implemented using outdated code which cleaned valid pages as well by mistake. The fix uses the original implementation from mm layer which was tweaked to zero pages after releasing them. Also it is now invoked only when there is HW encryption (meaning that cache is unencrypted) Change-Id: If713fd17a89be6d998c1a7ba86bee4c17d2bca0f Signed-off-by: Andrey Markovytch <andreym@codeaurora.org> Conflicts: fs/ecryptfs/caches_utils.c
This commit is contained in:
parent
b61ac21fb0
commit
03bad71331
5 changed files with 28 additions and 90 deletions
|
@ -5,6 +5,6 @@
|
|||
obj-$(CONFIG_ECRYPT_FS) += ecryptfs.o
|
||||
|
||||
ecryptfs-y := dentry.o file.o inode.o main.o super.o mmap.o read_write.o events.o \
|
||||
crypto.o keystore.o kthread.o debug.o caches_utils.o
|
||||
crypto.o keystore.o kthread.o debug.o
|
||||
|
||||
ecryptfs-$(CONFIG_ECRYPT_FS_MESSAGING) += messaging.o miscdev.o
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/pagevec.h>
|
||||
|
||||
#include "../internal.h"
|
||||
|
||||
void ecryptfs_drop_pagecache_sb(struct super_block *sb, void *unused)
|
||||
{
|
||||
struct inode *inode, *toput_inode = NULL;
|
||||
|
||||
spin_lock(&sb->s_inode_list_lock);
|
||||
list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
|
||||
spin_lock(&inode->i_lock);
|
||||
if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) ||
|
||||
(inode->i_mapping->nrpages == 0)) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
continue;
|
||||
}
|
||||
__iget(inode);
|
||||
spin_unlock(&inode->i_lock);
|
||||
spin_unlock(&sb->s_inode_list_lock);
|
||||
invalidate_mapping_pages(inode->i_mapping, 0, -1);
|
||||
iput(toput_inode);
|
||||
toput_inode = inode;
|
||||
spin_lock(&sb->s_inode_list_lock);
|
||||
}
|
||||
spin_unlock(&sb->s_inode_list_lock);
|
||||
iput(toput_inode);
|
||||
}
|
||||
|
||||
void clean_inode_pages(struct address_space *mapping,
|
||||
pgoff_t start, pgoff_t end)
|
||||
{
|
||||
struct pagevec pvec;
|
||||
pgoff_t index = start;
|
||||
int i;
|
||||
|
||||
pagevec_init(&pvec, 0);
|
||||
while (index <= end && pagevec_lookup(&pvec, mapping, index,
|
||||
min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
|
||||
for (i = 0; i < pagevec_count(&pvec); i++) {
|
||||
struct page *page = pvec.pages[i];
|
||||
|
||||
/**
|
||||
* We rely upon deletion
|
||||
* not changing page->index
|
||||
*/
|
||||
index = page->index;
|
||||
if (index > end)
|
||||
break;
|
||||
if (!trylock_page(page))
|
||||
continue;
|
||||
WARN_ON(page->index != index);
|
||||
zero_user(page, 0, PAGE_CACHE_SIZE);
|
||||
unlock_page(page);
|
||||
}
|
||||
pagevec_release(&pvec);
|
||||
cond_resched();
|
||||
index++;
|
||||
}
|
||||
}
|
|
@ -484,11 +484,13 @@ static void init_ecryption_parameters(bool *hw_crypt, bool *cipher_supported,
|
|||
ecryptfs_get_full_cipher(crypt_stat->cipher,
|
||||
crypt_stat->cipher_mode, final, sizeof(final)));
|
||||
if (*cipher_supported) {
|
||||
|
||||
/**
|
||||
* we should apply external algorythm
|
||||
* assume that is_hw_crypt() cbck is supplied
|
||||
*/
|
||||
*hw_crypt = get_events()->is_hw_crypt_cb();
|
||||
if (get_events()->is_hw_crypt_cb)
|
||||
*hw_crypt = get_events()->is_hw_crypt_cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -284,21 +284,10 @@ static int ecryptfs_flush(struct file *file, fl_owner_t td)
|
|||
|
||||
static int ecryptfs_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
|
||||
int ret;
|
||||
ret = vfs_fsync(file, false);
|
||||
|
||||
if (ret)
|
||||
pr_err("failed to sync file ret = %d.\n", ret);
|
||||
|
||||
ecryptfs_put_lower_file(inode);
|
||||
kmem_cache_free(ecryptfs_file_info_cache,
|
||||
ecryptfs_file_to_private(file));
|
||||
|
||||
clean_inode_pages(inode->i_mapping, 0, -1);
|
||||
clean_inode_pages(ecryptfs_inode_to_lower(inode)->i_mapping, 0, -1);
|
||||
truncate_inode_pages(inode->i_mapping, 0);
|
||||
truncate_inode_pages(ecryptfs_inode_to_lower(inode)->i_mapping, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -156,16 +156,36 @@ int ecryptfs_get_lower_file(struct dentry *dentry, struct inode *inode)
|
|||
|
||||
void ecryptfs_put_lower_file(struct inode *inode)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ecryptfs_inode_info *inode_info;
|
||||
bool clear_cache_needed = false;
|
||||
|
||||
inode_info = ecryptfs_inode_to_private(inode);
|
||||
if (atomic_dec_and_mutex_lock(&inode_info->lower_file_count,
|
||||
&inode_info->lower_file_mutex)) {
|
||||
|
||||
if (get_events() && get_events()->is_hw_crypt_cb &&
|
||||
get_events()->is_hw_crypt_cb())
|
||||
clear_cache_needed = true;
|
||||
|
||||
if (clear_cache_needed) {
|
||||
ret = vfs_fsync(inode_info->lower_file, false);
|
||||
|
||||
if (ret)
|
||||
pr_err("failed to sync file ret = %d.\n", ret);
|
||||
}
|
||||
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
fput(inode_info->lower_file);
|
||||
inode_info->lower_file = NULL;
|
||||
mutex_unlock(&inode_info->lower_file_mutex);
|
||||
|
||||
if (clear_cache_needed) {
|
||||
truncate_inode_pages_fill_zero(inode->i_mapping, 0);
|
||||
truncate_inode_pages_fill_zero(
|
||||
ecryptfs_inode_to_lower(inode)->i_mapping, 0);
|
||||
}
|
||||
|
||||
if (get_events() && get_events()->release_cb)
|
||||
get_events()->release_cb(
|
||||
ecryptfs_inode_to_lower(inode));
|
||||
|
@ -594,7 +614,10 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags
|
|||
|
||||
ecryptfs_set_superblock_lower(s, path.dentry->d_sb);
|
||||
|
||||
ecryptfs_drop_pagecache_sb(ecryptfs_superblock_to_lower(s), 0);
|
||||
|
||||
if (get_events() && get_events()->is_hw_crypt_cb &&
|
||||
get_events()->is_hw_crypt_cb())
|
||||
drop_pagecache_sb(ecryptfs_superblock_to_lower(s), 0);
|
||||
|
||||
/**
|
||||
* Set the POSIX ACL flag based on whether they're enabled in the lower
|
||||
|
|
Loading…
Add table
Reference in a new issue