442 lines
11 KiB
C
Executable File
442 lines
11 KiB
C
Executable File
/**
|
|
* sdp_cache.c
|
|
*
|
|
* Store inode number of all the sensitive files that have been opened since booting
|
|
*/
|
|
#include <linux/kthread.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/list.h>
|
|
#include <linux/pagemap.h>
|
|
|
|
#include "../fscrypt_private.h"
|
|
|
|
extern int dek_is_locked(int engine_id);
|
|
|
|
static struct kmem_cache *cachep;
|
|
|
|
LIST_HEAD(list_head);
|
|
static spinlock_t list_lock;
|
|
|
|
struct _entry {
|
|
u32 engine_id;
|
|
struct super_block *sb;
|
|
unsigned long ino; // inode number
|
|
struct list_head list;
|
|
};
|
|
|
|
static void *dump_entry_list_locked(const char *msg)
|
|
{
|
|
struct list_head *e;
|
|
int i = 0;
|
|
|
|
DEK_LOGD("%s(%s) : ", __func__, msg);
|
|
spin_lock(&list_lock);
|
|
list_for_each(e, &list_head) {
|
|
struct _entry *entry = list_entry(e, struct _entry, list);
|
|
|
|
if (entry)
|
|
DEK_LOGD("[%d: %p %lu] ", i, entry->sb, entry->ino);
|
|
i++;
|
|
}
|
|
spin_unlock(&list_lock);
|
|
DEK_LOGD("\n");
|
|
DEK_LOGE("%s(%s) : entry-num(%d)", __func__, msg, i);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct _entry *find_entry_locked(struct super_block *sb, unsigned long ino)
|
|
{
|
|
struct list_head *e;
|
|
|
|
spin_lock(&list_lock);
|
|
list_for_each(e, &list_head) {
|
|
struct _entry *entry = list_entry(e, struct _entry, list);
|
|
|
|
if (entry->sb == sb)
|
|
if (entry->ino == ino) {
|
|
spin_unlock(&list_lock);
|
|
return entry;
|
|
}
|
|
}
|
|
spin_unlock(&list_lock);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int add_entry_locked(u32 engine_id, struct super_block *sb, unsigned long ino)
|
|
{
|
|
struct _entry *entry = NULL;
|
|
|
|
DEK_LOGD("%s(sb:%p, ino:%lu) entered\n", __func__, sb, ino);
|
|
if (find_entry_locked(sb, ino))
|
|
return -EEXIST;
|
|
entry = kmem_cache_alloc(cachep, GFP_KERNEL);
|
|
if (!entry)
|
|
return -ENOMEM;
|
|
|
|
memset(entry, 0, sizeof(struct _entry));
|
|
INIT_LIST_HEAD(&entry->list);
|
|
entry->engine_id = engine_id;
|
|
entry->sb = sb;
|
|
entry->ino = ino;
|
|
spin_lock(&list_lock);
|
|
list_add_tail(&entry->list, &list_head);
|
|
spin_unlock(&list_lock);
|
|
|
|
dump_entry_list_locked("entry-added");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void _init(void *foo)
|
|
{
|
|
}
|
|
|
|
int fscrypt_sdp_cache_init(void)
|
|
{
|
|
spin_lock_init(&list_lock);
|
|
INIT_LIST_HEAD(&list_head);
|
|
|
|
cachep = kmem_cache_create("sdp_sensitive_ino_entry_cache",
|
|
sizeof(struct _entry),
|
|
0,
|
|
(SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|
|
|
SLAB_MEM_SPREAD),
|
|
_init);
|
|
|
|
if (cachep == NULL) {
|
|
DEK_LOGE("%s failed\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void fscrypt_sdp_cache_add_inode_num(struct inode *inode)
|
|
{
|
|
struct fscrypt_info *ci = inode->i_crypt_info;//This pointer must be loaded by get_encryption_info completely
|
|
if (ci && ci->ci_sdp_info) {
|
|
int res = add_entry_locked(ci->ci_sdp_info->engine_id, inode->i_sb, inode->i_ino);
|
|
if (!res) {
|
|
ci->ci_sdp_info->sdp_flags |= SDP_IS_INO_CACHED;
|
|
DEK_LOGD("%s(ino:%lu) sb:%p res:%d\n", __func__, inode->i_ino, inode->i_sb, res);
|
|
}
|
|
}
|
|
}
|
|
|
|
void fscrypt_sdp_cache_remove_inode_num(struct inode *inode)
|
|
{
|
|
if (inode) {
|
|
struct list_head *e, *temp;
|
|
struct fscrypt_info *ci = inode->i_crypt_info;
|
|
|
|
spin_lock(&list_lock);
|
|
// list_for_each(e, &list_head) {
|
|
list_for_each_safe(e, temp, &list_head) {
|
|
struct _entry *entry = list_entry(e, struct _entry, list);
|
|
|
|
if (entry->sb == inode->i_sb) {
|
|
if (entry->ino == inode->i_ino) {
|
|
list_del(&entry->list);
|
|
if (ci && ci->ci_sdp_info) {
|
|
ci->ci_sdp_info->sdp_flags &= ~(SDP_IS_INO_CACHED);
|
|
}
|
|
spin_unlock(&list_lock);
|
|
kmem_cache_free(cachep, entry);
|
|
DEK_LOGD("%s(ino:%lu) sb:%p\n", __func__, inode->i_ino, inode->i_sb);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
spin_unlock(&list_lock);
|
|
}
|
|
}
|
|
|
|
struct inode_drop_task_param {
|
|
int engine_id;
|
|
};
|
|
|
|
static unsigned long invalidate_mapping_pages_retry(struct address_space *mapping,
|
|
pgoff_t start, pgoff_t end, int retries)
|
|
{
|
|
DEK_LOGD("freeing [%s] sensitive inode[mapped pagenum = %lu]\n",
|
|
mapping->host->i_sb->s_type->name,
|
|
mapping->nrpages);
|
|
retry:
|
|
invalidate_mapping_pages(mapping, start, end);
|
|
DEK_LOGD("invalidate_mapping_pages [%lu] remained\n",
|
|
mapping->nrpages);
|
|
|
|
if (mapping->nrpages != 0) {
|
|
if (retries > 0) {
|
|
DEK_LOGD("[%lu] mapped pages remained in sensitive inode, retry..\n",
|
|
mapping->nrpages);
|
|
retries--;
|
|
msleep(100);
|
|
goto retry;
|
|
}
|
|
}
|
|
|
|
return mapping->nrpages;
|
|
}
|
|
|
|
static void wait_file_io_retry(struct inode *inode)
|
|
{
|
|
bool ret;
|
|
|
|
retry:
|
|
ret = fscrypt_sdp_is_cache_releasable(inode);
|
|
|
|
if (!ret) {
|
|
msleep(100);
|
|
goto retry;
|
|
}
|
|
}
|
|
|
|
static int inode_drop_task(void *arg)
|
|
{
|
|
struct inode_drop_task_param *param = arg;
|
|
int engine_id = param->engine_id;
|
|
|
|
struct _entry *entry, *entry_safe;
|
|
struct inode *inode;
|
|
struct fscrypt_info *ci;
|
|
LIST_HEAD(drop_list);
|
|
|
|
DEK_LOGD("%s(engine_id:%d) entered\n", __func__, engine_id);
|
|
dump_entry_list_locked("inode_drop");
|
|
|
|
spin_lock(&list_lock);
|
|
list_for_each_entry_safe(entry, entry_safe, &list_head, list) {
|
|
if (entry && entry->engine_id == engine_id) {
|
|
list_del_init(&entry->list);
|
|
list_add_tail(&entry->list, &drop_list);
|
|
}
|
|
}
|
|
spin_unlock(&list_lock);
|
|
|
|
if (!list_empty(&drop_list)) {
|
|
//Traverse local list and operate safely
|
|
list_for_each_entry_safe(entry, entry_safe, &drop_list, list) {
|
|
inode = ilookup(entry->sb, entry->ino);
|
|
if (!inode) {
|
|
DEK_LOGD("%s inode(%lu) not found\n", __func__, entry->ino);
|
|
goto err;
|
|
}
|
|
|
|
DEK_LOGD("%s found ino:%lu sb:%p\n", __func__, entry->ino, entry->sb);
|
|
ci = inode->i_crypt_info;
|
|
/*
|
|
* Instead of occuring BUG, skip the clearing only
|
|
* TODO: Must research later whether we can skip the logic in this case
|
|
BUG_ON(!ci);
|
|
BUG_ON(!ci->ci_sdp_info);
|
|
*/
|
|
if (!ci || !ci->ci_sdp_info) {
|
|
DEK_LOGD("%s May be already cleared\n", __func__);
|
|
goto free_icnt;
|
|
}
|
|
|
|
DEK_LOGD("%s found ino:%lu engine_id:%d sdp_flags:%x\n",
|
|
__func__, entry->ino, ci->ci_sdp_info->engine_id, ci->ci_sdp_info->sdp_flags);
|
|
|
|
if ((ci->ci_sdp_info->sdp_flags & SDP_DEK_IS_SENSITIVE) == 0) {
|
|
DEK_LOGE("%s not sensitive file\n", __func__);
|
|
goto free_icnt;
|
|
}
|
|
|
|
wait_file_io_retry(inode);
|
|
if (filemap_write_and_wait(inode->i_mapping))
|
|
DEK_LOGD("May failed to writeback");
|
|
|
|
DEK_LOGD("%s invalidating...\n", __func__);
|
|
if (invalidate_mapping_pages_retry(inode->i_mapping, 0, -1, 3) > 0) {
|
|
DEK_LOGE("Failed to invalidate entire pages..");
|
|
}
|
|
fscrypt_sdp_unset_clearing_ongoing(inode);
|
|
free_icnt:
|
|
iput(inode);
|
|
err:
|
|
list_del(&entry->list);
|
|
kmem_cache_free(cachep, entry);
|
|
}
|
|
}
|
|
|
|
DEK_LOGD("%s complete...\n", __func__);
|
|
dump_entry_list_locked("inode_drop(complete)");
|
|
|
|
kzfree(param);
|
|
return 0;
|
|
}
|
|
|
|
void fscrypt_sdp_cache_drop_inode_mappings(int engine_id)
|
|
{
|
|
struct task_struct *task;
|
|
struct inode_drop_task_param *param =
|
|
kzalloc(sizeof(*param), GFP_KERNEL);
|
|
|
|
param->engine_id = engine_id;
|
|
task = kthread_run(inode_drop_task, param, "fscrypt_sdp_drop_cached");
|
|
if (IS_ERR(task)) {
|
|
DEK_LOGE("unable to create kernel thread fscrypt_sdp_drop_cached res:%ld\n",
|
|
PTR_ERR(task));
|
|
}
|
|
}
|
|
|
|
int fscrypt_sdp_file_not_readable(struct file *file)
|
|
{
|
|
struct address_space *mapping = file->f_mapping;
|
|
struct inode *inode = mapping->host;
|
|
struct fscrypt_info *ci = inode->i_crypt_info;
|
|
int retval = 0;
|
|
|
|
if (ci && ci->ci_sdp_info) {
|
|
if (!S_ISDIR(inode->i_mode) && (ci->ci_sdp_info->sdp_flags & SDP_DEK_IS_SENSITIVE)) {
|
|
int is_locked = dek_is_locked(ci->ci_sdp_info->engine_id);
|
|
if (is_locked) {
|
|
wait_file_io_retry(inode);
|
|
if (filemap_write_and_wait(inode->i_mapping))
|
|
DEK_LOGD("May failed to writeback");
|
|
|
|
invalidate_mapping_pages_retry(inode->i_mapping, 0, -1, 3);
|
|
fscrypt_sdp_cache_remove_inode_num(inode);
|
|
fscrypt_sdp_unset_clearing_ongoing(inode);
|
|
} else {
|
|
int is_ino_cached = 0;
|
|
|
|
spin_lock(&ci->ci_sdp_info->sdp_flag_lock);
|
|
if (ci->ci_sdp_info->sdp_flags & SDP_IS_CLEARING_ONGOING) {
|
|
is_locked = 1;
|
|
} else {
|
|
ci->ci_sdp_info->sdp_flags |= SDP_IS_FILE_IO_ONGOING;
|
|
}
|
|
if (ci->ci_sdp_info->sdp_flags & SDP_IS_INO_CACHED)
|
|
is_ino_cached = 1;
|
|
spin_unlock(&ci->ci_sdp_info->sdp_flag_lock);
|
|
|
|
if (!is_ino_cached)
|
|
fscrypt_sdp_cache_add_inode_num(inode);
|
|
}
|
|
retval = is_locked;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int fscrypt_sdp_file_not_writable(struct file *file)
|
|
{
|
|
struct address_space *mapping = file->f_mapping;
|
|
struct inode *inode = mapping->host;
|
|
struct fscrypt_info *ci = inode->i_crypt_info;
|
|
int retval = 0;
|
|
|
|
if (!S_ISDIR(inode->i_mode) && ci && ci->ci_sdp_info &&
|
|
(ci->ci_sdp_info->sdp_flags & SDP_DEK_IS_SENSITIVE)) {
|
|
spin_lock(&ci->ci_sdp_info->sdp_flag_lock);
|
|
if (ci->ci_sdp_info->sdp_flags & SDP_IS_CLEARING_ONGOING) {
|
|
retval = -EINVAL;
|
|
} else {
|
|
ci->ci_sdp_info->sdp_flags |= SDP_IS_FILE_IO_ONGOING;
|
|
}
|
|
spin_unlock(&ci->ci_sdp_info->sdp_flag_lock);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
void fscrypt_sdp_unset_file_io_ongoing(struct file *file)
|
|
{
|
|
struct address_space *mapping = file->f_mapping;
|
|
struct inode *inode = mapping->host;
|
|
struct fscrypt_info *ci = inode->i_crypt_info;
|
|
|
|
if (ci && ci->ci_sdp_info && (ci->ci_sdp_info->sdp_flags & SDP_DEK_IS_SENSITIVE)) {
|
|
ci->ci_sdp_info->sdp_flags &= ~(SDP_IS_FILE_IO_ONGOING);
|
|
}
|
|
}
|
|
|
|
void fscrypt_sdp_unset_clearing_ongoing(struct inode *inode)
|
|
{
|
|
struct fscrypt_info *ci = inode->i_crypt_info;
|
|
|
|
if (ci && ci->ci_sdp_info && (ci->ci_sdp_info->sdp_flags & SDP_DEK_IS_SENSITIVE)) {
|
|
ci->ci_sdp_info->sdp_flags &= ~(SDP_IS_CLEARING_ONGOING);
|
|
}
|
|
}
|
|
|
|
bool fscrypt_sdp_is_cache_releasable(struct inode *inode)
|
|
{
|
|
bool retval = false;
|
|
struct fscrypt_info *ci = inode->i_crypt_info;
|
|
|
|
if (ci && ci->ci_sdp_info && (ci->ci_sdp_info->sdp_flags & SDP_DEK_IS_SENSITIVE)) {
|
|
spin_lock(&ci->ci_sdp_info->sdp_flag_lock);
|
|
if (ci->ci_sdp_info->sdp_flags & SDP_IS_FILE_IO_ONGOING ||
|
|
ci->ci_sdp_info->sdp_flags & SDP_IS_CLEARING_ONGOING) {
|
|
retval = false;
|
|
} else {
|
|
ci->ci_sdp_info->sdp_flags |= SDP_IS_CLEARING_ONGOING;
|
|
retval = true;
|
|
}
|
|
spin_unlock(&ci->ci_sdp_info->sdp_flag_lock);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
bool fscrypt_sdp_is_locked_sensitive_inode(struct inode *inode)
|
|
{
|
|
struct fscrypt_info *ci = inode->i_crypt_info;
|
|
|
|
if (ci && ci->ci_sdp_info &&
|
|
(ci->ci_sdp_info->sdp_flags & SDP_DEK_IS_SENSITIVE)) {
|
|
DEK_LOGD("Time to drop the inode..\n");
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
int __fscrypt_sdp_d_delete(const struct dentry *dentry, int dek_is_locked) {
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
if (inode && dek_is_locked) {
|
|
struct fscrypt_info *crypt_info = inode->i_crypt_info;
|
|
|
|
if (crypt_info && crypt_info->ci_sdp_info &&
|
|
crypt_info->ci_sdp_info->sdp_flags & SDP_DEK_IS_SENSITIVE) {
|
|
DEK_LOGD("Time to delete dcache....\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(__fscrypt_sdp_d_delete);
|
|
|
|
int fscrypt_sdp_d_delete_wrapper(const struct dentry *dentry) {
|
|
struct inode *inode = d_inode(dentry);
|
|
struct fscrypt_info *ci = inode ? inode->i_crypt_info : NULL;
|
|
#if 1
|
|
int is_locked = 1; /* TODO: */
|
|
#else
|
|
int is_locked = (ci && ci->ci_sdp_info) ? dek_is_locked(ci->ci_sdp_info->engine_id) : 0;
|
|
#endif
|
|
|
|
if (ci && __fscrypt_sdp_d_delete(dentry, is_locked))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void fscrypt_sdp_drop_inode(struct inode *inode) {
|
|
inode->i_state |= I_WILL_FREE;
|
|
spin_unlock(&inode->i_lock);
|
|
write_inode_now(inode, 1);
|
|
spin_lock(&inode->i_lock);
|
|
WARN_ON(inode->i_state & I_NEW);
|
|
inode->i_state &= ~I_WILL_FREE;
|
|
}
|