diff options
author | syn <isaqtm@gmail.com> | 2020-01-30 20:31:23 +0300 |
---|---|---|
committer | syn <isaqtm@gmail.com> | 2020-01-30 20:31:23 +0300 |
commit | 6662cea6b2572cd70a60aadfb28312e2bbef6ecc (patch) | |
tree | 1a4a527af10c11ba47224efd14cf866158ca774e | |
download | sad-6662cea6b2572cd70a60aadfb28312e2bbef6ecc.tar.gz |
Faulty first implementation of echo module
-rw-r--r-- | .gitignore | 10 | ||||
-rw-r--r-- | Makefile | 18 | ||||
-rw-r--r-- | assoc_buf.h | 72 | ||||
-rw-r--r-- | mod_add.c | 162 |
4 files changed, 262 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0e9079 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.o +*.ko +*.mod +*.mod.c +*.ko.cmd +*.mod.cmd +*o.cmd +*.symvers +modules.order + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8fd4a5d --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +obj-m += mod_add.o +all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +ins: + sudo insmod mod_add.ko + +rm: + sudo rmmod mod_add + sudo rm -f dev + +nod: all ins + sudo mknod dev c 238 0 + sudo chmod 666 dev + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean + diff --git a/assoc_buf.h b/assoc_buf.h new file mode 100644 index 0000000..e9e7fbc --- /dev/null +++ b/assoc_buf.h @@ -0,0 +1,72 @@ +#include <linux/slab.h> +#include <linux/kernel.h> + +struct asbuf { + unsigned long ino; + char *buf; + size_t alloc; +}; + + +ssize_t asbuf_free_alloc(struct asbuf *buf, size_t size) { + char *mem; + + mem = kmalloc(size, GFP_KERNEL); + if (!mem) + return -ENOMEM; + + if (buf->alloc) + kfree(buf->buf); + + buf->buf = mem; + buf->alloc = size; + return 0; +} + +void asbuf_free(struct asbuf *buf) { + kfree(buf->buf); +} + +ssize_t asbuf_to_user(struct asbuf *buf, + char __user *user_buf, + size_t len, + loff_t *offset) { + + size_t chars_left, request; + ssize_t uncopied, copied; + + if (!buf->alloc) + return -EINVAL; + + if (*offset > buf->alloc) + return -EINVAL; + + chars_left = buf->alloc - *offset; + request = min(len, chars_left); + + printk(KERN_INFO "buf: %p\t alloc: %zu\t request: %zu\n", buf, buf->alloc, request); + + uncopied = copy_to_user(user_buf, buf->buf + *offset, request); + copied = request - uncopied; + + *offset += copied; + return copied; +} + +ssize_t asbuf_from_user(struct asbuf *buf, + const char __user *user_buf, + size_t len, + loff_t *offset) { + ssize_t rc = 0; + size_t uncopied, copied; + rc = asbuf_free_alloc(buf, len); + if (rc != 0) + return rc; + + uncopied = copy_from_user(buf->buf, user_buf, len); + copied = len - uncopied; + + *offset = 0; + + return copied; +} diff --git a/mod_add.c b/mod_add.c new file mode 100644 index 0000000..cfabc50 --- /dev/null +++ b/mod_add.c @@ -0,0 +1,162 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +MODULE_LICENSE("GPL"); + +#include "assoc_buf.h" + +#define DEV_NAME "mod_add" +#define OP_INVALID "Operation invalid\n" + +static int device_open(struct inode *, struct file *); +static int device_release(struct inode *, struct file *); +static ssize_t device_read(struct file *, char *, size_t, loff_t *); +static ssize_t device_write(struct file *, const char *, size_t, loff_t *); + +static int major_num; +static int device_open_count = 0; + +static struct buf_list { + struct asbuf *buf; + struct buf_list *next; +} *buffers; + +struct asbuf *find_by_inode(unsigned long ino) { + struct buf_list *iter = buffers->next; + while (iter) { + if (iter->buf->ino == ino) + return iter->buf; + iter = iter->next; + } + return NULL; +} + +ssize_t add_buf(struct asbuf *buf) { + struct buf_list *iter; + + if (!buffers) { + buffers = kmalloc(sizeof(struct buf_list), GFP_KERNEL); + if (!buffers) + return -ENOMEM; + buffers->buf = buf; + buffers->next = NULL; + return 0; + } + + iter = buffers; + while (iter->next) + iter = iter->next; + + iter->next = kmalloc(sizeof(struct buf_list), GFP_KERNEL); + if (!iter->next) + return -ENOMEM; + iter->buf = buf; + iter->next = NULL; + return 0; +} + +void free_buf_list(void) { + struct buf_list *iter = buffers, *next; + + while (iter) { + next = iter->next; + asbuf_free(iter->buf); + kfree(iter->buf); + kfree(iter); + iter = next; + } +} + +ssize_t rm_by_inode(unsigned long ino) { + struct buf_list *iter = buffers; + struct buf_list *save_prev; + + if (!buffers) + return 0; + + while (iter->next) { + save_prev = iter; + iter = iter->next; + if (iter->buf->ino == ino) { + save_prev->next = iter->next; + asbuf_free(iter->buf); + kfree(iter->buf); + kfree(iter); + return 0; + } + } + return -EINVAL; +} + +static struct file_operations file_ops = { + .read = device_read, + .write = device_write, + .open = device_open, + .release = device_release +}; + + +static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offset) { + struct asbuf *buf = find_by_inode(flip->f_inode->i_ino); + printk(KERN_INFO "req inode %lu\n", flip->f_inode->i_ino); + if (!buf) + return -EINVAL; + return asbuf_to_user(buf, buffer, len, offset); +} + +static ssize_t device_write(struct file *flip, const char *buffer, size_t len, loff_t *offset) { + if (len == 0) + return 0; + + return asbuf_from_user(find_by_inode(flip->f_inode->i_ino), buffer, len, offset); +} + +static int device_open(struct inode *inode, struct file *file) { + struct asbuf *newbuf; + + if (device_open_count) + return -EBUSY; + + newbuf = kzalloc(sizeof(*newbuf), GFP_KERNEL); + if (!newbuf) + return -ENOMEM; + + printk(KERN_INFO "opened for inode %lu", inode->i_ino); + newbuf->ino = inode->i_ino; + add_buf (newbuf); + device_open_count++; + try_module_get(THIS_MODULE); + return 0; +} + +static int device_release(struct inode *inode, struct file *file) { + rm_by_inode(inode->i_ino); + device_open_count--; + module_put(THIS_MODULE); + return 0; +} + +static int __init mod_add_init(void) { + buffers = kzalloc(sizeof(*buffers), GFP_KERNEL); + + major_num = register_chrdev(0, DEV_NAME, &file_ops); + if (major_num < 0) { + printk(KERN_ALERT "Could not register device: %d\n", major_num); + return major_num; + } else { + printk(KERN_INFO "mod_add loaded. dev maj = %d\n", major_num); + return 0; + } +} +static void __exit mod_add_exit(void) { + unregister_chrdev(major_num, DEV_NAME); + free_buf_list(); + printk(KERN_INFO "okay, bye\n"); +} + +module_init(mod_add_init); +module_exit(mod_add_exit); |