blob: f71adc1515733fdc86d2fd200076bccd9a155263 [file] [log] [blame]
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/uio.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/interrupt.h>
extern char *d_path(const struct path *, char *, int);
static struct file_operations org_operations;
static struct file_operations hooked_operations;
static struct inode_operations org_inode_operations;
static struct file_operations *p_curr_file_operations;
static char opt_file_name[256] = {0};
static char * hooked_files[32] = {
"/media/download/mdm.download",
"/media/download/sbl.download",
"/media/download/mcu_a.download",
"/media/download/mcu_b.download",
"/media/download/ble.download",
"/media/download/file.download",
"/media/download/runtime_file",
"/media/download/swk1",
"/media/download/swk2",
0,
};
static int g_hooked_file_cnt = 0;
static spinlock_t my_lock;
static struct work_struct my_work;
static struct work_struct rename_file_work;
static loff_t g_next_pos;
static loff_t g_ki_pos;
static bool g_scheduled = false;
static struct file * g_hooked_file_filp;
static const char * g_hooked_file_name;
static char g_hooked_tmp_file[256];
static bool g_hooked_tmp_file_writen;
static char * g_last_hook_file = NULL;
struct cached_items_type
{
struct file * filp;
void * cached_buffer;
int cached_buffer_len;
loff_t curr_offset;
void * allocate_ptr;
};
struct cached_buffer_type
{
void * cached_buffer;
void * ptr;
};
#define BUF_CNT_MASK 0xff
#define ITEM_CNT_MASK 0xfff
static struct cached_items_type cached_items[ITEM_CNT_MASK + 1] = {0};
static struct cached_buffer_type g_buffers[BUF_CNT_MASK+1] = {0};
static int head_pos = 0;
static int tail_pos = 0;
static int max_cnt = 0;
static int buffer_head_pos = 0;
static int buffer_tail_pos = BUF_CNT_MASK;
static size_t init_and_push_item(struct file *filp, struct kiocb *iocb, struct iov_iter *from)
{
unsigned long flags;
int index = (tail_pos) & ITEM_CNT_MASK;
int ret, buffer_index;
loff_t local_pos = 0;
size_t cnt = 0;
//if (iocb != NULL)
if (g_next_pos != -1)
{
if (g_next_pos != g_ki_pos)
printk("cy: next pos %ld current :%ld\n", g_next_pos);
local_pos = g_next_pos;
g_next_pos = -1;
// if (local_pos != 0 && g_ki_pos != local_pos) {
// printk("cy: got pos %ld, curr %ld\n", local_pos, g_ki_pos);
// }
}
else
{
local_pos = g_ki_pos;
}
cnt = iov_iter_count(from);
if (buffer_head_pos >= buffer_tail_pos || cnt > 16*PAGE_SIZE)
{
cached_items[index].allocate_ptr = kmalloc((cnt/PAGE_SIZE + 2)*PAGE_SIZE, GFP_KERNEL);
if (cached_items[index].allocate_ptr == NULL)
return 0;
cached_items[index].cached_buffer = (char*)((((long long)cached_items[index].allocate_ptr)/PAGE_SIZE + 1)*PAGE_SIZE);
}
else
{
buffer_index = (buffer_head_pos++ & BUF_CNT_MASK);
cached_items[index].allocate_ptr = NULL;
cached_items[index].cached_buffer = g_buffers[buffer_index].cached_buffer;
}
cached_items[index].filp = filp;
ret = copy_from_iter(cached_items[index].cached_buffer, cnt, from);
cached_items[index].cached_buffer_len = cnt;
cached_items[index].curr_offset = local_pos;
g_ki_pos = local_pos + cnt;
tail_pos++;
if (!g_scheduled)
{
g_scheduled = true;
schedule_work(&my_work);
}
return cnt;
}
struct cached_items_type * get_head_item(void)
{
if (head_pos >= tail_pos)
return NULL;
return &cached_items[head_pos++ & ITEM_CNT_MASK];
}
static int my_ext4_file_open(struct inode * inode, struct file * filp)
{
int i, j, ret, found;
unsigned long flags;
struct file * local_file;
char buff[256];
ret = org_operations.open(inode, filp);
//ret = 0;
if (ret == 0)
{
found = 0;
for(i=0; i<32 && found < g_hooked_file_cnt; i++) {
if (hooked_files[i] != NULL && strcmp(hooked_files[i], file_path(filp, buff, 256)) == 0)
{
printk("found hooked file11, %s write ptr %p private_data %p\n", hooked_files[i], filp->f_op->write, filp->private_data);
for(j=0;j<5;j++) {
if (g_hooked_file_filp != NULL)
usleep_range(2000,2100);
else
break;
}
if (g_hooked_file_filp != NULL)
{
if (g_last_hook_file == NULL || strcmp(hooked_files[i], g_last_hook_file) != 0)
break;
for(j=0;j<100;j++) {
if (g_hooked_file_filp != NULL)
usleep_range(20000,21000);
else
break;
}
}
spin_lock_irqsave(&my_lock, flags);
head_pos = 0;
tail_pos = 0;
max_cnt = 0;
g_ki_pos = 0;
buffer_head_pos = 0;
buffer_tail_pos = BUF_CNT_MASK;
g_last_hook_file = hooked_files[i];
p_curr_file_operations->open = org_operations.open;
spin_unlock_irqrestore(&my_lock, flags);
g_hooked_tmp_file_writen = false;
g_hooked_file_name = hooked_files[i];
sprintf(g_hooked_tmp_file, "%s.dl", hooked_files[i]);
printk("cy: after name is %s\n", g_hooked_tmp_file);
local_file = filp_open(g_hooked_tmp_file, O_RDWR | O_CREAT | O_TRUNC, 0644);
spin_lock_irqsave(&my_lock, flags);
if (!IS_ERR(local_file))
{
g_hooked_file_filp = local_file;
filp->private_data = local_file;
filp->f_op = &hooked_operations;
}
else
{
g_hooked_file_filp = NULL;
}
p_curr_file_operations->open = my_ext4_file_open;
spin_unlock_irqrestore(&my_lock, flags);
break;
}
else if (hooked_files[i] != NULL) {
found++;
}
}
//dump_stack();
}
return ret;
}
static loff_t my_ext4_llseek(struct file *file, loff_t offset, int whence)
{
//printk("cy: %s, %ld, %d\n", __func__, offset, whence);
if (whence == SEEK_SET) {
g_next_pos = offset;
if (g_hooked_tmp_file_writen)
return offset;
else
return org_operations.llseek(file, offset, whence);
}
else
{
printk("unhandle %d\n", whence);
return org_operations.llseek(file, offset, whence);
}
}
static ssize_t my_ext4_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
printk("cy: %s\n", __func__);
return org_operations.read_iter(iocb, to);
}
static ssize_t my_ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
int i, ret;
int left_cnt, waited = 0;
loff_t write_cnt=0;
for (i=0; i<40;i++)
{
left_cnt = tail_pos - head_pos;
if ( left_cnt> max_cnt){
max_cnt = left_cnt;
if ((max_cnt & 0x1f) == 0)
printk("cy: got max %d", max_cnt);
}
if (left_cnt < ITEM_CNT_MASK)
{
break;
}
if (!g_scheduled)
{
g_scheduled = true;
schedule_work(&my_work);
}
waited = 1;
usleep_range(1000,1100);
}
if (i >= 40) {
printk("cy: write fail\n");
return -1;
}
else if (i > 1){
printk("cy: got full\n");
}
//printk("cy: my_ext4_file_write_iter %p\n", iocb->ki_filp->private_data);
if (iocb->ki_filp->private_data != NULL)
{
write_cnt = init_and_push_item(iocb->ki_filp->private_data, iocb, from);
g_hooked_tmp_file_writen = true;
}
// if (left_cnt > 64)
// usleep_range(7000,7100);
// else if (left_cnt > 32)
// usleep_range(5000,5100);
// else if (left_cnt > 8)
// usleep_range(3000,3100);
// else if (left_cnt > 1 && !waited)
// usleep_range(500,600);
return write_cnt;
}
static int my_ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{
printk("cy: %s\n", __func__);
return org_operations.fsync(file, start, end, datasync);
}
static int my_ext4_release_file(struct inode *inode, struct file *filp)
{
int i, ret;
// struct file *local_filp=NULL;
char buff[256];
char buff2[256];
char * file_name;
//printk("cy: %s\n", __func__);
if (filp == NULL)
return -1;
// dump_stack();
for (i=0; i<40;i++)
{
if (tail_pos == head_pos)
break;
usleep_range(300,350);
}
//printk("cy: close file wait count %d\n", i);
if (filp->private_data != NULL)
{
filp->private_data = 0;
}
ret = org_operations.release(inode, filp);
if (g_hooked_file_filp != NULL)
{
schedule_work(&rename_file_work);
}
return ret;
}
static void my_callback(struct work_struct *work)
{
ssize_t ret;
loff_t local_pos = 0;
struct cached_items_type * p;
p = get_head_item();
// if (p != NULL)
// usleep_range(200,250);
while(p != NULL) {
local_pos = p->curr_offset;
ret = __kernel_write(p->filp, p->cached_buffer, p->cached_buffer_len, &local_pos);
//g_ki_pos = p->curr_offset + p->cached_buffer_len;
if (p->allocate_ptr)
{
kfree(p->allocate_ptr);
p->allocate_ptr = NULL;
p->cached_buffer = NULL;
}
else
{
buffer_tail_pos++;
p->cached_buffer = NULL;
}
p = get_head_item();
}
g_scheduled = false;
}
static void rename_file_callback(struct work_struct *work)
{
int ret, i;
struct path old_path, new_path;
struct dentry *old_dentry, *new_dentry;
if (g_hooked_file_filp == NULL)
return;
printk("cy4: close ld file finish\n");
if (!g_hooked_tmp_file_writen)
{
ret = filp_close(g_hooked_file_filp, 0);
g_hooked_file_filp = NULL;
return;
}
printk("cy: head pos %d, tail pos %d\n", head_pos, tail_pos);
if (!g_scheduled) // write thread not running, check again
{
printk("cy: not g_scheduled\n");
my_callback(&my_work);
}
for (i=0; i<5000;i++)
{
if (head_pos == tail_pos)
{
break;
}
if (!g_scheduled)
{
printk("cy: not g_scheduled 2\n");
my_callback(&my_work);
break;
}
usleep_range(1000,1100);
if ((i & 0x3ff) == 0 )
printk("cy: wait file write done, sleep again\n");
}
printk("cy: wait times %d\n", i);
ret = filp_close(g_hooked_file_filp, 0);
g_hooked_file_filp = NULL;
ret = kern_path(g_hooked_tmp_file, LOOKUP_PARENT, &old_path);
if (ret != 0)
goto out2;
ret = kern_path(g_hooked_file_name, LOOKUP_PARENT, &new_path);
if (ret != 0)
goto out1;
old_dentry = old_path.dentry;
new_dentry = new_path.dentry;
if (old_dentry != NULL && new_dentry != NULL && old_path.dentry->d_parent != NULL && new_path.dentry->d_parent != NULL)
{
ret = vfs_rename(old_path.dentry->d_parent->d_inode, old_dentry, new_path.dentry->d_parent->d_inode, new_dentry, NULL, RENAME_EXCHANGE);
}
else
{
printk("cy: got null ptr\n");
}
path_put(&new_path);
out1:
path_put(&old_path);
out2:
g_hooked_tmp_file_writen = false;
printk("cy:rename file finish\n");
}
#define DEBUGFS_READ_FUNC(name) \
static ssize_t asr_dbgfs_##name##_read(struct file *file, \
char __user *user_buf, \
size_t count, loff_t *ppos);
#define DEBUGFS_WRITE_FUNC(name) \
static ssize_t asr_dbgfs_##name##_write(struct file *file, \
const char __user *user_buf,\
size_t count, loff_t *ppos);
#define DEBUGFS_ADD_FILE(name, parent, mode) do { \
if (!debugfs_create_file(#name, mode, parent, NULL, \
&asr_dbgfs_##name##_ops)) \
goto err; \
} while (0)
#define DEBUGFS_WRITE_FILE_OPS(name) \
DEBUGFS_WRITE_FUNC(name); \
static const struct file_operations asr_dbgfs_##name##_ops = { \
.write = asr_dbgfs_##name##_write, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_READ_FILE_OPS(name) \
DEBUGFS_READ_FUNC(name); \
static const struct file_operations asr_dbgfs_##name##_ops = { \
.read = asr_dbgfs_##name##_read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
static void strip_enter(char * buf, int len){
int i;
for(i=0;i<len;i++) {
if (buf[i] == '\r' || buf[i] == '\n' || buf[i]=='\0') {
buf[i] = '\0';
return;
}
}
}
static ssize_t asr_dbgfs_add_hook_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
int i;
char buf[256]={0};
size_t len = min_t(size_t, sizeof(buf) - 1, count);
printk("cy: add hook count %d\n", count);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
strip_enter(buf, 256);
printk("cy: add hook buf %s\n", buf);
for(i=0;i<32;i++) {
if (hooked_files[i] == NULL) {
hooked_files[i] = kmalloc(count+1, GFP_KERNEL);
strcpy(hooked_files[i], buf);
g_hooked_file_cnt++;
break;
}
}
if (i >= 32)
return -EFAULT;
return count;
}
DEBUGFS_WRITE_FILE_OPS(add_hook);
static ssize_t asr_dbgfs_del_hook_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
int i;
char buf[256]={0};
size_t len = min_t(size_t, sizeof(buf) - 1, count);
printk("cy: del hook count %d\n", count);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
strip_enter(buf, 256);
printk("cy: del hook buf %s\n", buf);
for(i=0;i<32;i++) {
if (hooked_files[i] != NULL && strcmp(hooked_files[i], buf) == 0) {
hooked_files[i] = NULL;
g_hooked_file_cnt--;
break;
}
}
if (i >= 32)
return -EFAULT;
return count;
}
DEBUGFS_WRITE_FILE_OPS(del_hook);
static ssize_t asr_dbgfs_show_all_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
int i, found, ret, read;
char *buf;
int bufsz = 256 * g_hooked_file_cnt;
buf = kmalloc(bufsz, GFP_ATOMIC);
if (buf == NULL)
return 0;
found = 0;
ret = 0;
for(i=0;i<32 && found < g_hooked_file_cnt;i++) {
if (hooked_files[i] != NULL) {
found++;
ret += scnprintf(&buf[ret], 258, "%s\n", hooked_files[i]);
}
}
read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
kfree(buf);
return read;
}
DEBUGFS_READ_FILE_OPS(show_all);
static struct dentry * hook_root=NULL;
static int asr_dbgfs_register(void)
{
if (!(hook_root = debugfs_create_dir("opt_cmd_36",NULL)))
return -1;
DEBUGFS_ADD_FILE(add_hook, hook_root, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(del_hook, hook_root, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(show_all, hook_root, S_IRUSR);
return 0;
err:
debugfs_remove_recursive(hook_root);
return -1;
}
static void asr_dbgfs_unregister(void)
{
if (hook_root != NULL)
{
debugfs_remove_recursive(hook_root);
hook_root = NULL;
}
}
static int __init opt_cmd_36_init(void)
{
int ret, i;
unsigned long flags;
struct file *filp;
char * ptr;
char *filename = "/media/.test_create";
g_hooked_file_filp = NULL;
g_hooked_tmp_file_writen = false;
for(i=0; i<32;i++) {
if (hooked_files[i] != NULL) {
printk("cy: got %s\n", hooked_files[i]);
g_hooked_file_cnt++;
}
else if (strlen(opt_file_name) > 0)
{
hooked_files[i] = opt_file_name;
g_hooked_file_cnt++;
break;
}
}
filp = filp_open(filename, O_RDONLY | O_CREAT, 0644);
if (IS_ERR(filp) || filp->f_op == NULL) {
printk(KERN_ERR "Failed to open file: %ld\n", PTR_ERR(filp));
return -1;
}
printk("cy11: opt_cmd_36_init param [%s]\n", opt_file_name);
INIT_WORK(&my_work, my_callback);
INIT_WORK(&rename_file_work, rename_file_callback);
for(i=0;i<=BUF_CNT_MASK;i++)
{
g_buffers[i].ptr = kmalloc(32*PAGE_SIZE, GFP_KERNEL);
ptr = (char*)((((long long)g_buffers[i].ptr)/PAGE_SIZE + 1)*PAGE_SIZE);
g_buffers[i].cached_buffer = ptr;
//cached_items[i].org_ptr = kmalloc(32*PAGE_SIZE, GFP_KERNEL);
//ptr = (char*)((((long long)cached_items[i].org_ptr)/PAGE_SIZE + 1)*PAGE_SIZE);
//cached_items[i].cached_buffer = ptr;
}
spin_lock_irqsave(&my_lock, flags);
p_curr_file_operations = filp->f_op;
org_operations.llseek = p_curr_file_operations->llseek;
org_operations.read_iter = p_curr_file_operations->read_iter;
org_operations.write_iter = p_curr_file_operations->write_iter;
org_operations.unlocked_ioctl = p_curr_file_operations->unlocked_ioctl;
#ifdef CONFIG_COMPAT
org_operations.compat_ioctl = p_curr_file_operations->compat_ioctl;
#endif
org_operations.mmap = p_curr_file_operations->mmap;
org_operations.mmap_supported_flags = p_curr_file_operations->mmap_supported_flags;
org_operations.open = p_curr_file_operations->open;
org_operations.release = p_curr_file_operations->release;
org_operations.fsync = p_curr_file_operations->fsync;
org_operations.get_unmapped_area = p_curr_file_operations->get_unmapped_area;
org_operations.splice_read = p_curr_file_operations->splice_read;
org_operations.splice_write = p_curr_file_operations->splice_write;
org_operations.fallocate = p_curr_file_operations->fallocate;
hooked_operations.llseek = my_ext4_llseek;
//hooked_operations.write = hooked_write;
//hooked_operations.read_iter = my_ext4_file_read_iter;
hooked_operations.read_iter = p_curr_file_operations->read_iter;
hooked_operations.write_iter = my_ext4_file_write_iter;
//hooked_operations.write_iter = p_curr_file_operations->write_iter;
hooked_operations.unlocked_ioctl = p_curr_file_operations->unlocked_ioctl;
#ifdef CONFIG_COMPAT
hooked_operations.compat_ioctl = p_curr_file_operations->compat_ioctl;
#endif
hooked_operations.mmap = p_curr_file_operations->mmap;
hooked_operations.mmap_supported_flags = p_curr_file_operations->mmap_supported_flags;
hooked_operations.open = p_curr_file_operations->open;
//hooked_operations.release = p_curr_file_operations->release;
hooked_operations.release = my_ext4_release_file;
//hooked_operations.fsync = p_curr_file_operations->fsync;
hooked_operations.fsync = my_ext4_sync_file;
hooked_operations.get_unmapped_area = p_curr_file_operations->get_unmapped_area;
hooked_operations.splice_read = p_curr_file_operations->splice_read;
hooked_operations.splice_write = p_curr_file_operations->splice_write;
hooked_operations.fallocate = p_curr_file_operations->fallocate;
p_curr_file_operations->open = my_ext4_file_open;
spin_unlock_irqrestore(&my_lock, flags);
ret = filp_close(filp, 0);
asr_dbgfs_register();
pr_debug("init done\n");
return 0;
}
static void __exit opt_cmd_36_exit(void)
{
int i;
unsigned long flags;
spin_lock_irqsave(&my_lock, flags);
p_curr_file_operations->open = org_operations.open;
spin_unlock_irqrestore(&my_lock, flags);
for(i=0;i<=BUF_CNT_MASK;i++)
{
// if (cached_items[i].org_ptr != NULL)
// kfree(cached_items[i].org_ptr);
if (g_buffers[i].ptr != NULL)
kfree(g_buffers[i].ptr);
}
asr_dbgfs_unregister();
pr_debug("exit done\n");
}
module_param_string(opt_file_name, opt_file_name, 256, 0);
module_init(opt_cmd_36_init)
module_exit(opt_cmd_36_exit)
MODULE_LICENSE("GPL");