[RFC 7/8] kmsg: add ioctl for adding and deleting kmsg* devices

Marcin Niesluchowski
2015-07-03T10:51:49Z

There is no possibility to add/delete kmsg* buffers from userspace.

Adds following ioctl for main kmsg device adding and deleting
additional kmsg devices:
* KMSG_CMD_BUFFER_ADD
* KMSG_CMD_BUFFER_DEL

Signed-off-by: Marcin Niesluchowski <m.niesluchow@samsung.com>
---
Documentation/ioctl/ioctl-number.txt | 1 +
drivers/char/mem.c | 134 +++++++++++++++++++++++++++++++++--
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/kmsg_ioctl.h | 30 ++++++++
4 files changed, 159 insertions(+), 7 deletions(-)
create mode 100644 include/uapi/linux/kmsg_ioctl.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 611c522..26c0e53 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -312,6 +312,7 @@ Code Seq#(hex) Include File Comments
<mailto:vgo@ratio.de>
0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca>
0xB3 00 linux/mmc/ioctl.h
+0xBB 00-02 uapi/linux/kmsg_ioctl.h
0xC0 00-0F linux/usb/iowarrior.h
0xCA 00-0F uapi/misc/cxl.h
0xCB 00-1F CBM serial IEC bus in development:
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 8d5ba0d..2893d8e 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -34,8 +34,14 @@
# include <linux/efi.h>
#endif

+#ifdef CONFIG_PRINTK
+#include <linux/kmsg_ioctl.h>
+#endif
+
#define DEVPORT_MINOR 4

+static struct class *mem_class;
+
static inline unsigned long size_inside_page(unsigned long start,
unsigned long size)
{
@@ -715,6 +721,113 @@ static int open_port(struct inode *inode, struct file *filp)
return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
}

+#ifdef CONFIG_PRINTK
+#define KMSG_MINOR 11
+
+#define MAX_MINOR_LEN 20
+
+static int kmsg_open_ext(struct inode *inode, struct file *file)
+{
+ return kmsg_fops.open(inode, file);
+}
+
+static ssize_t kmsg_write_iter_ext(struct kiocb *iocb, struct iov_iter *from)
+{
+ return kmsg_fops.write_iter(iocb, from);
+}
+
+static ssize_t kmsg_read_ext(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return kmsg_fops.read(file, buf, count, ppos);
+}
+
+static loff_t kmsg_llseek_ext(struct file *file, loff_t offset, int whence)
+{
+ return kmsg_fops.llseek(file, offset, whence);
+}
+
+static unsigned int kmsg_poll_ext(struct file *file,
+ struct poll_table_struct *wait)
+{
+ return kmsg_fops.poll(file, wait);
+}
+
+static long kmsg_ioctl_buffers(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ size_t size;
+ umode_t mode;
+ char name[4 + MAX_MINOR_LEN + 1];
+ struct device *dev;
+ int minor;
+
+ if (iminor(file->f_inode) != KMSG_MINOR)
+ return -ENOTTY;
+
+ switch (cmd) {
+ case KMSG_CMD_BUFFER_ADD:
+ if (copy_from_user(&size, argp, sizeof(size)))
+ return -EFAULT;
+ argp += sizeof(size);
+ if (copy_from_user(&mode, argp, sizeof(mode)))
+ return -EFAULT;
+ argp += sizeof(mode);
+ minor = kmsg_sys_buffer_add(size, mode);
+ if (minor < 0)
+ return minor;
+ sprintf(name, "kmsg%d", minor);
+ dev = device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
+ NULL, name);
+ if (IS_ERR(dev)) {
+ kmsg_sys_buffer_del(minor);
+ return PTR_ERR(dev);
+ }
+ if (copy_to_user(argp, &minor, sizeof(minor))) {
+ device_destroy(mem_class, MKDEV(MEM_MAJOR, minor));
+ kmsg_sys_buffer_del(minor);
+ return -EFAULT;
+ }
+ return 0;
+ case KMSG_CMD_BUFFER_DEL:
+ if (copy_from_user(&minor, argp, sizeof(minor)))
+ return -EFAULT;
+ if (minor <= KMSG_MINOR)
+ return -EINVAL;
+ device_destroy(mem_class, MKDEV(MEM_MAJOR, minor));
+ kmsg_sys_buffer_del(minor);
+ return 0;
+ }
+ return -ENOTTY;
+}
+
+static long kmsg_unlocked_ioctl_ext(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ long ret = kmsg_ioctl_buffers(file, cmd, arg);
+
+ if (ret == -ENOTTY)
+ return kmsg_fops.unlocked_ioctl(file, cmd, arg);
+ return ret;
+}
+
+static long kmsg_compat_ioctl_ext(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ long ret = kmsg_ioctl_buffers(file, cmd, arg);
+
+ if (ret == -ENOTTY)
+ return kmsg_fops.compat_ioctl(file, cmd, arg);
+ return ret;
+}
+
+static int kmsg_release_ext(struct inode *inode, struct file *file)
+{
+ return kmsg_fops.release(inode, file);
+}
+#endif
+
#define zero_lseek null_lseek
#define full_lseek null_lseek
#define write_zero write_null
@@ -779,6 +892,19 @@ static const struct file_operations full_fops = {
.write = write_full,
};

+#ifdef CONFIG_PRINTK
+static const struct file_operations kmsg_fops_ext = {
+ .open = kmsg_open_ext,
+ .read = kmsg_read_ext,
+ .write_iter = kmsg_write_iter_ext,
+ .llseek = kmsg_llseek_ext,
+ .poll = kmsg_poll_ext,
+ .unlocked_ioctl = kmsg_unlocked_ioctl_ext,
+ .compat_ioctl = kmsg_compat_ioctl_ext,
+ .release = kmsg_release_ext,
+};
+#endif
+
static const struct memdev {
const char *name;
umode_t mode;
@@ -800,14 +926,10 @@ static const struct memdev {
[8] = { "random", 0666, &random_fops, 0 },
[9] = { "urandom", 0666, &urandom_fops, 0 },
#ifdef CONFIG_PRINTK
- [11] = { "kmsg", 0644, &kmsg_fops, 0 },
+ [11] = { "kmsg", 0644, &kmsg_fops_ext, 0 },
#endif
};

-#ifdef CONFIG_PRINTK
-#define KMSG_MINOR 11
-#endif
-
static int memory_open(struct inode *inode, struct file *filp)
{
int minor;
@@ -858,8 +980,6 @@ static char *mem_devnode(struct device *dev, umode_t *mode)
return NULL;
}

-static struct class *mem_class;
-
static int __init chr_dev_init(void)
{
int minor;
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 1ff9942..faf13a8 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -224,6 +224,7 @@ header-y += kernel-page-flags.h
header-y += kexec.h
header-y += keyboard.h
header-y += keyctl.h
+header-y += kmsg_ioctl.h

ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/uapi/asm/kvm.h \
$(srctree)/arch/$(SRCARCH)/include/asm/kvm.h),)
diff --git a/include/uapi/linux/kmsg_ioctl.h b/include/uapi/linux/kmsg_ioctl.h
new file mode 100644
index 0000000..89c0c61
--- /dev/null
+++ b/include/uapi/linux/kmsg_ioctl.h
@@ -0,0 +1,30 @@
+/*
+ * This is ioctl include for kmsg* devices
+ */
+
+#ifndef _KMSG_IOCTL_H_
+#define _KMSG_IOCTL_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+struct kmsg_cmd_buffer_add {
+ size_t size;
+ unsigned short mode;
+ int minor;
+} __attribute__((packed));
+
+#define KMSG_IOCTL_MAGIC 0xBB
+
+/*
+ * A ioctl interface for kmsg device.
+ *
+ * KMSG_CMD_BUFFER_ADD: Creates additional kmsg device based on its size
+ * and mode. Minor of created device is put.
+ * KMSG_CMD_BUFFER_DEL: Removes additional kmsg device based on its minor
+ */
+#define KMSG_CMD_BUFFER_ADD _IOWR(KMSG_IOCTL_MAGIC, 0x00, \
+ struct kmsg_cmd_buffer_add)
+#define KMSG_CMD_BUFFER_DEL _IOW(KMSG_IOCTL_MAGIC, 0x01, int)
+
+#endif
--
1.9.1