summaryrefslogtreecommitdiffstats
path: root/drivers/block/loop/loop_file_fmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/block/loop/loop_file_fmt.c')
-rw-r--r--drivers/block/loop/loop_file_fmt.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/drivers/block/loop/loop_file_fmt.c b/drivers/block/loop/loop_file_fmt.c
new file mode 100644
index 000000000000..f29c15ed044b
--- /dev/null
+++ b/drivers/block/loop/loop_file_fmt.c
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * loop_file_fmt.c
+ *
+ * File format subsystem for the loop device module.
+ *
+ * Copyright (C) 2019 Manuel Bentele
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "loop_file_fmt.h"
+
+/* storage for all registered file format drivers */
+static struct loop_file_fmt_driver *loop_file_fmt_drivers[MAX_LO_FILE_FMT] = {
+ NULL
+};
+
+int loop_file_fmt_register_driver(struct loop_file_fmt_driver *drv)
+{
+ int ret = 0;
+
+ if (drv == NULL)
+ return -EFAULT;
+
+ if (drv->file_fmt_type > MAX_LO_FILE_FMT)
+ return -EINVAL;
+
+ if (loop_file_fmt_drivers[drv->file_fmt_type] == NULL) {
+ loop_file_fmt_drivers[drv->file_fmt_type] = drv;
+ printk(KERN_INFO "loop: successfully registered file format "
+ "driver %s\n", drv->name);
+ } else {
+ printk(KERN_WARNING "loop: driver for file format already "
+ "registered\n");
+ ret = -EBUSY;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(loop_file_fmt_register_driver);
+
+void loop_file_fmt_unregister_driver(struct loop_file_fmt_driver *drv)
+{
+ if (drv == NULL)
+ return;
+
+ if (drv->file_fmt_type > MAX_LO_FILE_FMT)
+ return;
+
+ loop_file_fmt_drivers[drv->file_fmt_type] = NULL;
+ printk(KERN_INFO "loop: successfully unregistered file format driver "
+ "%s\n", drv->name);
+}
+EXPORT_SYMBOL(loop_file_fmt_unregister_driver);
+
+struct loop_file_fmt *loop_file_fmt_alloc(void)
+{
+ return kzalloc(sizeof(struct loop_file_fmt), GFP_KERNEL);
+}
+
+void loop_file_fmt_free(struct loop_file_fmt *lo_fmt)
+{
+ kfree(lo_fmt);
+}
+
+int loop_file_fmt_set_lo(struct loop_file_fmt *lo_fmt, struct loop_device *lo)
+{
+ if (lo_fmt == NULL)
+ return -EINVAL;
+
+ lo_fmt->lo = lo;
+
+ return 0;
+}
+EXPORT_SYMBOL(loop_file_fmt_set_lo);
+
+struct loop_device *loop_file_fmt_get_lo(struct loop_file_fmt *lo_fmt)
+{
+ return lo_fmt->lo;
+}
+EXPORT_SYMBOL(loop_file_fmt_get_lo);
+
+int loop_file_fmt_init(struct loop_file_fmt *lo_fmt)
+{
+ struct loop_file_fmt_ops* ops;
+ struct module *drv;
+
+ if (lo_fmt->file_fmt_type > MAX_LO_FILE_FMT)
+ return -EINVAL;
+
+ /* check if new file format driver is registered */
+ if (loop_file_fmt_drivers[lo_fmt->file_fmt_type] == NULL) {
+ printk(KERN_ERR "loop_file_fmt: file format driver is not "
+ "available\n");
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "loop_file_fmt: use file format driver %s\n",
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->name);
+
+ drv = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->owner;
+ if (!try_module_get(drv)) {
+ printk(KERN_ERR "loop_file_fmt: file format driver %s can not "
+ "be accessed\n",
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->name);
+ return -ENODEV;
+ }
+
+ ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+ if (likely(ops->init))
+ return ops->init(lo_fmt);
+ else
+ return -ENOSYS;
+}
+
+void loop_file_fmt_exit(struct loop_file_fmt *lo_fmt)
+{
+ struct loop_file_fmt_ops* ops;
+ struct module *drv;
+
+ ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+ if (likely(ops->exit))
+ ops->exit(lo_fmt);
+
+ drv = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->owner;
+ module_put(drv);
+}
+
+int loop_file_fmt_read(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->read))
+ return ops->read(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_read_aio(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->read_aio))
+ return ops->read_aio(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_write(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->write))
+ return ops->write(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_write_aio(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->write_aio))
+ return ops->write_aio(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_discard(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->discard))
+ return ops->discard(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_flush(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->flush))
+ return ops->flush(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_change(struct loop_file_fmt *lo_fmt,
+ __u32 file_fmt_type_new)
+{
+ if (file_fmt_type_new > MAX_LO_FILE_FMT)
+ return -EINVAL;
+
+ /*
+ * Unload the old file format driver and after that,
+ * load the new driver
+ */
+ loop_file_fmt_exit(lo_fmt);
+ lo_fmt->file_fmt_type = file_fmt_type_new;
+ printk(KERN_ERR "loop: change file format driver");
+ return loop_file_fmt_init(lo_fmt);
+}
+
+ssize_t loop_file_fmt_print_type(__u32 file_fmt_type, char *file_fmt_name)
+{
+ ssize_t len = 0;
+
+ switch(file_fmt_type) {
+ case LO_FILE_FMT_RAW:
+ len = sprintf(file_fmt_name, "%s", "RAW");
+ break;
+ case LO_FILE_FMT_QCOW:
+ len = sprintf(file_fmt_name, "%s", "QCOW");
+ break;
+ case LO_FILE_FMT_VDI:
+ len = sprintf(file_fmt_name, "%s", "VDI");
+ break;
+ case LO_FILE_FMT_VMDK:
+ len = sprintf(file_fmt_name, "%s", "VMDK");
+ break;
+ default:
+ len = sprintf(file_fmt_name, "%s", "ERROR: Unsupported loop "
+ "file format!");
+ break;
+ }
+
+ return len;
+}
+EXPORT_SYMBOL(loop_file_fmt_print_type);