| |
| #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"); |