--- /dev/null
+#undef __KERNEL__
+#define __KERNEL__
+#undef MODULE
+#define MODULE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+
+struct fetch_fops_args {
+ int fd;
+ u64 retp;
+};
+
+#define MOCTEL_FETCH_FOPS _IOR('m', 1, struct fetch_fops_args)
+
+static int ioctl_open(struct inode *nodp, struct file *filp) {
+ return 0;
+}
+
+static const struct file_operations dummy_fops = {};
+
+static long ioctl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
+ struct fetch_fops_args args;
+ struct fd f;
+ const struct file_operations *fops;
+
+ if (cmd != MOCTEL_FETCH_FOPS) return -EINVAL;
+ if (copy_from_user(&args, (struct fetch_fops_args __user *)arg, sizeof(args))) return -EINVAL;
+ f = fdget(args.fd);
+ if (!f.file) return -EBADF;
+ fops = f.file->f_op;
+ if (!fops) fops = dummy_fops;
+ fdput(f);
+ if (copy_to_user((struct file_operations __user *)args.retp, fops, sizeof(*fops))) return -EINVAL;
+ return 0;
+}
+
+static int ioctl_release(struct inode *nodp, struct file *filp) {
+ return 0;
+}
+
+static const struct file_operations ioctl_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = ioctl_ioctl, /* I have no idea about locking,
+ so just take the Big Kernel Lock implicitly. */
+ .open = ioctl_open,
+ .release = ioctl_release
+};
+
+static struct miscdevice ioctl_miscdev = {
+ .minor = 0,
+ .name = "ioctl_info",
+ .fops = &ioctl_fops
+};
+
+static int __init init_ioctl() {
+ int ret;
+ printk(KERN_INFO "loading ioctl helper\n");
+
+ ret = misc_register(&ioctl_miscdev);
+ return ret;
+}
+
+static void __exit cleanup_ioctl() {
+ printk(KERN_INFO "unloading ioctl helper\n");
+ misc_deregister(&ioctl_miscdev);
+}
+
+module_init(init_ioctl);
+module_exit(cleanup_ioctl);