From 03bad71331e87b52d377cf7fcdab547f6c744902 Mon Sep 17 00:00:00 2001 From: Andrey Markovytch Date: Sun, 7 Feb 2016 14:27:25 +0200 Subject: [PATCH] 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 Conflicts: fs/ecryptfs/caches_utils.c --- fs/ecryptfs/Makefile | 2 +- fs/ecryptfs/caches_utils.c | 76 -------------------------------------- fs/ecryptfs/crypto.c | 4 +- fs/ecryptfs/file.c | 11 ------ fs/ecryptfs/main.c | 25 ++++++++++++- 5 files changed, 28 insertions(+), 90 deletions(-) delete mode 100644 fs/ecryptfs/caches_utils.c diff --git a/fs/ecryptfs/Makefile b/fs/ecryptfs/Makefile index 995719c376a3..c29cdd20d08a 100644 --- a/fs/ecryptfs/Makefile +++ b/fs/ecryptfs/Makefile @@ -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 diff --git a/fs/ecryptfs/caches_utils.c b/fs/ecryptfs/caches_utils.c deleted file mode 100644 index 8bf9a534b5b0..000000000000 --- a/fs/ecryptfs/caches_utils.c +++ /dev/null @@ -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 -#include -#include -#include -#include - -#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++; - } -} diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index d0c5100d2c01..b98b786b0e9d 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -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(); } } } diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index bfcbe6f6876e..8c536c02e295 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -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; } diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 4565d6fa6cca..63298ba20478 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -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