This project is due on Thursday, April 23, at 11:59:59 PM (Eastern daylight time). You must use the submit to turn in your homework like so: submit cs421_jtang proj1 rot13.c
Your module code must be named rot13.c. It will be compiled against a 3.14 Linux kernel source tree, via the Kbuild supplied below. It must not have any compilation warnings; warnings will result in grading penalties.
In addition, your code must be properly indented and have a file header comment, as described on the coding conventions page. Prior to submission, use the kernel's indentation script to reformat your code, like so:
/usr/src/linux/scripts/Lindent rot13.c
In this project, you will write a Linux kernel module that will "encrypt" strings using the ROT-13 algorithm. Your module will create the character device /dev/rot13. Your module will store anything written to this device from user space, appending the input to an internal buffer. When something reads from the device, your module will return a ROT-13 encoding of the internal buffer, and then flush that buffer.
To begin, create a directory for your project and download the following three files into that directory via wget:
Now run make to compile the module. Upon success, you should now have the kernel module rot13.ko. Load that module like so:
sudo insmod rot13.ko (enter your password as necessary)The module was inserted if the following returns a non-empty string: lsmod | grep rot13.
So far, all this module does is write a message to the kernel's ring buffer. View the module messages like so:
dmesg | tailThe number at the beginning is the time stamp of when the message was written.
When the rot13 module is loaded, the kernel calls its init
function (similar to a C program's main() function) where
execution begins. The only thing this module's init function does
is call pr_info()
. The pr_info()
function
is an easy way to generate messages within kernel code. It accepts a
formatting string like printf()
.
Every time you make a change and recompile rot13.c, don't forget to first unload the module before reinserting it. Execute this command to unload the module:
sudo rmmod rot13Re-examine the ring buffer to see the message generated during module exit.
Creating a custom character device can be daunting. Fortunately, the Linux kernel has the miscellaneous devices subsystem to simplify this task. For this project, the rot13 module will use a miscdevice to control /dev/rot13.
Start off by adding the following stub functions prior to rot13_init():
#include <linux/fs.h> #include <linux/miscdevice.h> /** * rot13_open() - callback invoked when a process tries to open this * character device * @inode: inode of character device (ignored) * @filp: process's file object that is opening the device (ignored) * Return: 0 if open is permitted, negative on error */ static int rot13_open(struct inode *inode, struct file *filp) { return -EPERM; } /** * rot13_release() - callback invoked when a process closes this * character device * @inode: inode of character device (ignored) * @filp: process's file object that is closing the device (ignored) * Return: 0 on success, negative on error */ static int rot13_release(struct inode *inode, struct file *filp) { return -EPERM; } /** * rot13_read() - callback invoked when a process reads from this * character device * @filp: process's file object that is reading this device (ignored) * @ubuf: location to write ROT-13 of internal buffer * @count: number of bytes requested by process * @ppos: file offset (ignored) * * Write to @ubuf up to @count bytes from internal buffer, performing * a ROT-13 during write. If @count is greater than number of bytes * actually stored in internal buffer, truncate to internal buffer's * size. * * Regardless of number of bytes requested, clear entire internal * buffer afterwards. * * Return: number of bytes written to @ubuf, or negative on error */ static ssize_t rot13_read(struct file *filp, char __user * ubuf, size_t count, loff_t * ppos) { return -EPERM; } /** * rot13_read() - callback invoked when a process writes to this * character device * @filp: process's file object that is writing this device (ignored) * @ubuf: source buffer of bytes to copy into internal buffer * @count: number of bytes in @ubuf * @ppos: file offset (ignored) * * Read from @ubuf up to @count bytes and append to internal * buffer. If there is insufficient space to store @count bytes, store * as many as possible and ignore the rest. * * Return: @count, or negative on error */ static ssize_t rot13_write(struct file *filp, const char __user * ubuf, size_t count, loff_t * ppos) { return -EPERM; }
Next, you need
to create
the miscellaneous device and register these callbacks against the
device. Declare a static const struct
file_operations
with fields pointing to the above four
callbacks. Then declare a static struct miscdevice
referencing the above struct
file_operations
. In rot13_init()
,
call misc_register()
to create the character device,
and then in rot13_exit()
call misc_deregister()
to undo the operation.
If all of the above works, you will now have character device /dev/rot13 when the module is loaded:
$ sudo insmod rot13.ko $ ls -l /dev/rot13 crw-rw-rw- 1 root root 10, 55 Mar 14 12:34 /dev/rot13 $ cat /dev/rot13 cat: /dev/rot13: Operation not permittedBecause
rot13_open()
returns -EPERM
,
trying to open the device is supposed to result in permission
denied.
At the top of rot13.c, declare a global static array that
can hold up to 1024 characters (not including any trailing null
characters). Next,
implement rot13_open()
, rot13_release()
, rot13_read()
,
and rot13_write()
as per their comments. Note that in
rot13_read()
and rot13_write()
, the module cannot simply access the
buf pointer. Instead, the code must use copy_to_user()
and
copy_from_user()
to safely
copy data to and from user space into kernel space.
After implementing the four required callbacks, recompile and reinsert the module. Test it like so:
$ echo "I <3 CS421!" > /dev/rot13 $ echo "I am a 31337 programmer." > /dev/rot13 $ cat /dev/rot13 V <3 PF421! V nz n 31337 cebtenzzre.
The final step is to ensure your driver runs thread-safe. In theory,
multiple processes could read/write to the driver
simultaneously. The easiest way to solve this is to have a single
static spinlock. Use
that spinlock to lock the entire
rot13_read()
and rot13_write()
functions.
(The above spinlock blog post is out of date, as
that SPIN_LOCK_UNLOCKED
no longer exists in the 3.14
kernel. It has
been replaced
by the DEFINE_SPINLOCK
macro.)
struct
miscdevice
's .mode
field.
\0
, but
any 1024 characters.
Sorry, there is no extra credit available for this assignment.