Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 30eb189a authored by Andrey Markovytch's avatar Andrey Markovytch
Browse files

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: default avatarAndrey Markovytch <andreym@codeaurora.org>
parent 747b0f6a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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

fs/ecryptfs/caches_utils.c

deleted100644 → 0
+0 −81
Original line number Diff line number Diff line
/*
 * Copyright (C) 2002, Linus Torvalds
 *
 * 10Sep2002 Andrew Morton
 *
 * Copyright (c) 2015-2016, 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(&inode_sb_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(&inode_sb_list_lock);
		invalidate_mapping_pages(inode->i_mapping, 0, -1);
		iput(toput_inode);
		toput_inode = inode;
		spin_lock(&inode_sb_list_lock);
	}
	spin_unlock(&inode_sb_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++;
	}
}
+3 −1
Original line number Diff line number Diff line
@@ -485,10 +485,12 @@ 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
			 */
			if (get_events()->is_hw_crypt_cb)
				*hw_crypt = get_events()->is_hw_crypt_cb();
		}
	}
+0 −11
Original line number Diff line number Diff line
@@ -291,21 +291,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;
}

+24 −1
Original line number Diff line number Diff line
@@ -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