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:
Andrey Markovytch 2016-02-07 14:27:25 +02:00 committed by David Keitel
parent b61ac21fb0
commit 03bad71331
5 changed files with 28 additions and 90 deletions

View file

@ -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

View file

@ -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++;
}
}

View file

@ -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();
}
}
}

View file

@ -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;
}

View file

@ -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