Initial import of the muenevents Linux kernel module
[muen/linux/muenevents.git] / fs.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Muen source events file system.
4  *
5  * Copyright (C) 2020  secunet Security Networks AG
6  * Copyright (C) 2020  codelabs GmbH
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 /**
23  * @file fs.c
24  * @brief Implementation of the file system and the file operations.
25  *
26  * This kernel module implements a file system named "muenevents". It
27  * facilitates user-space to trigger source events (subject-specific
28  * hypercalls) provided by the Muen Separation Kernel.
29  *
30  * For each source event a file with the corresponding logical name is shown in
31  * the file system. A program can write to the files to trigger an event.
32  * Reading from a file returns the source event number.
33  *
34  * The current authentication model is that the files are created with uid and
35  * gid set to 0 and permissions 0600. No further capability checking is done by
36  * this module.
37  */
38
39 #include <linux/module.h>
40 #include <linux/uaccess.h>
41 #include <linux/pagemap.h>
42 #include <linux/slab.h>
43 #include <linux/io.h>
44 #include <linux/kvm_para.h>
45
46 #include "internal.h"
47
48 /**
49  * @brief Random magic value to identify this file system.
50  */
51 #define MUENEVENTS_MAGIC 0x9a9bc92b
52
53 /**
54  * @brief Size of temporary string buffer.
55  */
56 #define TMPBUFLEN 20
57
58 /**
59  * @brief Retrieve #source_event_t from file.
60  *
61  * This function retrieves the source event information previously stored with
62  * set_event_info().
63  *
64  * @param file file pointer
65  * @return the source_event_t stored in the private_data element.
66  */
67 static inline struct source_event_t *get_event_info(struct file *file)
68 {
69         return file->private_data;
70 }
71
72 /**
73  * @brief Store #source_event_t into file.
74  *
75  * This function uses the file private_data element to store the source event
76  * information.
77  *
78  * @param file file pointer
79  * @param info source event information
80  */
81 static inline void set_event_info(struct file *file,
82                                    struct source_event_t *info)
83 {
84         file->private_data = info;
85 }
86
87 /**
88  * @brief Open a file representing a source event.
89  *
90  * This function transfers the private data pointing to the
91  * source event from the inode to the file.
92  *
93  * @param inode inode information
94  * @param file file information
95  * @return 0 on success
96  */
97 static int muenevents_open(struct inode *inode, struct file *file)
98 {
99         struct source_event_t *cur_event = inode->i_private;
100
101         set_event_info(file, cur_event);
102         return 0;
103 }
104
105 /**
106  * @brief Read event number associated with given file.
107  *
108  * This function writes the source event number to the user area.
109  *
110  * @param file file pointer
111  * @param buffer user-space buffer to store the event number
112  * @param count maximum number of bytes to read
113  * @param offset pointer to current position
114  * @return number of bytes read
115  * @return -EFAULT on error
116  */
117 static ssize_t muenevents_read(struct file *file,
118                            char __user *buffer,
119                            size_t count,
120                            loff_t *offset)
121 {
122         const struct source_event_t * const cur_event = get_event_info(file);
123         int len;
124         char tmp[TMPBUFLEN];
125
126         len = snprintf(tmp, TMPBUFLEN, "%lu\n", cur_event->number);
127         if (len < 0)
128                 return -EFAULT;
129
130         if (*offset > len)
131                 return 0;
132
133         if (count > len - *offset)
134                 count = len - *offset;
135
136         if (copy_to_user(buffer, tmp + *offset, count))
137                 return -EFAULT;
138
139         *offset += count;
140         return count;
141 }
142
143 /**
144  * @brief Trigger source event identified by given file.
145  *
146  * This function triggers the source event associated with the file. Data
147  * provided by the user is ignored.
148  *
149  * @param file file pointer
150  * @param buffer user-space buffer, unused
151  * @param count maximum number of bytes to write
152  * @param offset pointer to current position, unused
153  * @return 1 on success
154  */
155 static ssize_t muenevents_write(struct file *file,
156                             const char __user *buffer,
157                             size_t count,
158                             loff_t *offset)
159 {
160         const struct source_event_t * const cur_event = get_event_info(file);
161
162         kvm_hypercall0(cur_event->number);
163         return count;
164 }
165
166 /**
167  * @brief File operations for this file system.
168  */
169 static const struct file_operations muenevents_file_fops = {
170         .open  = muenevents_open,
171         .read  = muenevents_read,
172         .write = muenevents_write,
173 };
174
175 /**
176  * @brief Create a new inode.
177  *
178  * @param sb super block of our file system
179  * @param mode the file mode to use (includes specification of the file type)
180  * @return a new inode on success
181  * @return NULL if inode could not be allocated
182  */
183 static struct inode *muenevents_make_inode(struct super_block *sb, int mode)
184 {
185         struct inode *ret = new_inode(sb);
186
187         if (ret) {
188                 ret->i_mode = mode;
189                 ret->i_uid.val = ret->i_gid.val = 0;
190                 ret->i_blocks = 0;
191                 ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret);
192         }
193         return ret;
194 }
195
196 /**
197  * @brief Callback parameter type.
198  */
199 struct cb_arg {
200         struct super_block *sb; /**< the super block of the file system    */
201         struct dentry *dir;     /**< the parent directory to create a file */
202 };
203
204 /**
205  * @brief Create a file.
206  *
207  * This function creates a new file in our file system. The event given as
208  * parameter is stored in the private data of the inode for later retrieval.
209  * File mode is set to 0600 for read-write access. The file size is set to 1
210  * byte.
211  *
212  * @param event the source event associated with the file
213  * @param data refers to the super block of the file system and the base
214  *        directory
215  * @return true on success
216  * @return false if directory entry could not be allocated
217  */
218 static bool muenevents_create_file(const struct muen_resource_type *const info,
219                                void *const data)
220 {
221         const int file_mode = 0600;
222         struct cb_arg *arg = data;
223         struct dentry *dentry;
224         struct inode *inode;
225         struct qstr qname;
226         struct source_event_t *event;
227
228         /* only export event resources, skip other resources */
229         if (info->kind != MUEN_RES_EVENT)
230                 return true;
231
232         event = kzalloc(sizeof(*event), GFP_KERNEL);
233         if (!event)
234                 goto out;
235
236         strncpy(event->name, info->name.data, info->name.length);
237         event->number = info->data.number;
238
239         /* create hashed name */
240         qname.name = event->name;
241         qname.len = strlen(event->name);
242         qname.hash = full_name_hash(arg->dir, event->name, qname.len);
243
244         inode = muenevents_make_inode(arg->sb, S_IFREG | file_mode);
245         if (!inode)
246                 goto out_free;
247
248         inode->i_ino = get_next_ino();
249         inode->i_size = 1;
250         inode->i_fop = &muenevents_file_fops;
251         inode->i_private = event;
252
253         dentry = d_alloc(arg->dir, &qname);
254         if (!dentry)
255                 goto out_iput;
256
257         /* put into cache and return */
258         d_add(dentry, inode);
259         pr_info("muenevents: registered file %s - event number %lu",
260                event->name, event->number);
261         return true;
262
263 out_iput:
264         iput(inode);
265 out_free:
266         kfree(event);
267 out:
268         return false;
269 }
270
271 /**
272  * @brief Create a file for each Muen source event
273  *
274  * This function creates a new file in the root directory for every Muen
275  * event that is present.
276  *
277  * @param sb super block of the file system
278  * @return 0 on success
279  * @return -ENOMEN on error
280  */
281 static int muenevents_create_files(struct super_block *sb)
282 {
283         struct cb_arg args = { .sb = sb, .dir = sb->s_root };
284
285         return muen_for_each_resource(muenevents_create_file, &args) ?
286                 0 : -ENOMEM;
287 }
288
289 /**
290  * @brief Fills the super block of the file system.
291  *
292  * This functions initializes the super block, creates the root directory and
293  * the files using the muenevents_create_files() function.
294  *
295  * @param sb the super block of the file system
296  * @param data file system options, currently ignored
297  * @param silent don't display any printk message if true (ignored)
298  * @return 0 on success
299  * @see simple_fill_super() and muenevents_create_files() for possible error
300  * conditions
301  */
302 static int muenevents_fill_super(struct super_block *sb, void *data, int silent)
303 {
304         static struct tree_descr empty_descr = {""};
305         int err;
306
307         err = simple_fill_super(sb, MUENEVENTS_MAGIC, &empty_descr);
308         if (err)
309                 return err;
310
311         return muenevents_create_files(sb);
312         /* TODO: Register dentry_operations to free
313          * allocated source event structures.
314          */
315 }
316
317 /**
318  * @brief Mount super block.
319  *
320  * This function uses mount_single() to provide a single instance of the file
321  * system. The function muenevents_fill_super() is specified to fill the super
322  * block of the instance.
323  *
324  * @param fst file system type specification
325  * @param flags parameters specified in the user-space for this mount operation
326  * @param devname device to mount, ignored
327  * @param data file system options specified in user-space
328  * @return pointer or error condition returned by mount_single()
329  */
330 static struct dentry *muenevents_mount(struct file_system_type *fst,
331                                   int flags, const char *devname, void *data)
332 {
333         return mount_single(fst, flags, data, muenevents_fill_super);
334 }
335
336 /**
337  * This description contains the owner, the name, the operation to get the
338  * super block, and the operation to destroy the super block. Here
339  * kill_litter_super() is required as we are holding references to the
340  * directory entries in the file system.
341  */
342 struct file_system_type muenevents_type = {
343         .owner          = THIS_MODULE,
344         .name           = "muenevents",
345         .mount          = muenevents_mount,
346         .kill_sb        = kill_litter_super,
347 };