forked from cirosantilli/linux-kernel-module-cheat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfops.c
138 lines (124 loc) · 3.28 KB
/
fops.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* https://cirosantilli.com/linux-kernel-module-cheat#file-operations */
#include <linux/debugfs.h>
#include <linux/errno.h> /* EFAULT */
#include <linux/fs.h> /* file_operations */
#include <linux/kernel.h> /* min */
#include <linux/module.h>
#include <linux/printk.h> /* printk */
#include <linux/uaccess.h> /* copy_from_user, copy_to_user */
#include <uapi/linux/stat.h> /* S_IRUSR */
static struct dentry *debugfs_file;
static char data[] = {'a', 'b', 'c', 'd'};
static int open(struct inode *inode, struct file *filp)
{
pr_info("open\n");
return 0;
}
/* @param[in,out] off: gives the initial position into the buffer.
* We must increment this by the ammount of bytes read.
* Then when userland reads the same file descriptor again,
* we start from that point instead.
*/
static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
ssize_t ret;
pr_info("read\n");
pr_info("len = %zu\n", len);
pr_info("off = %lld\n", (long long)*off);
if (sizeof(data) <= *off) {
ret = 0;
} else {
ret = min(len, sizeof(data) - (size_t)*off);
if (copy_to_user(buf, data + *off, ret)) {
ret = -EFAULT;
} else {
*off += ret;
}
}
pr_info("buf = %.*s\n", (int)len, buf);
pr_info("ret = %lld\n", (long long)ret);
return ret;
}
/* Similar to read, but with one notable difference:
* we must return ENOSPC if the user tries to write more
* than the size of our buffer. Otherwise, Bash > just
* keeps trying to write to it infinitely.
*/
static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
ssize_t ret;
pr_info("write\n");
pr_info("len = %zu\n", len);
pr_info("off = %lld\n", (long long)*off);
if (sizeof(data) <= *off) {
ret = 0;
} else {
if (sizeof(data) - (size_t)*off < len) {
ret = -ENOSPC;
} else {
if (copy_from_user(data + *off, buf, len)) {
ret = -EFAULT;
} else {
ret = len;
pr_info("buf = %.*s\n", (int)len, data + *off);
*off += ret;
}
}
}
pr_info("ret = %lld\n", (long long)ret);
return ret;
}
/* Called on the last close:
* http://stackoverflow.com/questions/11393674/why-is-the-close-function-is-called-release-in-struct-file-operations-in-the-l
*/
static int release(struct inode *inode, struct file *filp)
{
pr_info("release\n");
return 0;
}
static loff_t llseek(struct file *filp, loff_t off, int whence)
{
loff_t newpos;
pr_info("llseek\n");
pr_info("off = %lld\n", (long long)off);
pr_info("whence = %lld\n", (long long)whence);
switch(whence) {
case SEEK_SET:
newpos = off;
break;
case SEEK_CUR:
newpos = filp->f_pos + off;
break;
case SEEK_END:
newpos = sizeof(data) + off;
break;
default:
return -EINVAL;
}
if (newpos < 0) return -EINVAL;
filp->f_pos = newpos;
pr_info("newpos = %lld\n", (long long)newpos);
return newpos;
}
static const struct file_operations fops = {
/* Prevents rmmod while fops are running.
* Try removing this for poll, which waits a lot. */
.owner = THIS_MODULE,
.llseek = llseek,
.open = open,
.read = read,
.release = release,
.write = write,
};
static int myinit(void)
{
debugfs_file = debugfs_create_file("lkmc_fops", S_IRUSR | S_IWUSR, NULL, NULL, &fops);
return 0;
}
static void myexit(void)
{
debugfs_remove_recursive(debugfs_file);
}
module_init(myinit)
module_exit(myexit)
MODULE_LICENSE("GPL");