diff options
author | Manuel Bentele | 2020-09-07 14:16:19 +0200 |
---|---|---|
committer | Manuel Bentele | 2020-09-16 07:34:59 +0200 |
commit | 20beabd7fc25cab198fac90a79de6cf3e7339301 (patch) | |
tree | c8f4dee5d209fc09ea3135d48a14e66164313317 | |
parent | Cache last decompressed cluster for compressed QCOW (diff) | |
download | xloop-20beabd7fc25cab198fac90a79de6cf3e7339301.tar.gz xloop-20beabd7fc25cab198fac90a79de6cf3e7339301.tar.xz xloop-20beabd7fc25cab198fac90a79de6cf3e7339301.zip |
Added file format file format subsystem for loop devices
The loop device module is extended by a file format subsystem to allow the
implementation of various disk file formats. The file format drivers are
implemented as own kernel modules and registered by the subsystem. The
subsystem takes control over the specified file format at loop creation and
calls the corresponding file format driver functions.
At the moment, the file format subsystem can handle ...
- read
- write
- discard
- flush
- sector size
... operations of loop devices.
The file format of each loop device can be specified by the LOOP_CONFIGURE
LOOP_SET_STATUS or LOOP_SET_STATUS64 ioctl with the corresponding data
structure loop_info or respectively loop_info64.
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Kbuild.in | 10 | ||||
-rw-r--r-- | Kconfig | 16 | ||||
-rw-r--r-- | loop_file_fmt.c | 227 | ||||
-rw-r--r-- | loop_file_fmt.h | 281 | ||||
-rw-r--r-- | loop_file_fmt_qcow_cache.c | 74 | ||||
-rw-r--r-- | loop_file_fmt_qcow_cache.h | 24 | ||||
-rw-r--r-- | loop_file_fmt_qcow_cluster.c | 78 | ||||
-rw-r--r-- | loop_file_fmt_qcow_cluster.h | 8 | ||||
-rw-r--r-- | loop_file_fmt_qcow_main.c | 262 | ||||
-rw-r--r-- | loop_file_fmt_qcow_main.h | 72 | ||||
-rw-r--r-- | loop_file_fmt_raw.c | 252 | ||||
-rw-r--r-- | loop_main.c | 1934 | ||||
-rw-r--r-- | loop_main.h | 120 | ||||
-rw-r--r-- | uapi/linux/loop.h | 125 |
15 files changed, 1861 insertions, 1624 deletions
@@ -4,5 +4,5 @@ modules.order *.o *.o.d *.o.cmd -*.mod.c +*.mod* *.ko.cmd @@ -1,9 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_BLK_DEV_LOOP) += xloop.o -xloop-objs += loop_main.o loop_file_fmt.o +obj-$(CONFIG_BLK_DEV_XLOOP) += xloop.o +xloop-objs += loop_main.o loop_file_fmt.o -obj-$(CONFIG_BLK_DEV_LOOP_FILE_FMT_RAW) += loop_file_fmt_raw.o +obj-$(CONFIG_BLK_DEV_XLOOP_FILE_FMT_RAW) += loop_file_fmt_raw.o -loop_file_fmt_qcow-y += loop_file_fmt_qcow_main.o loop_file_fmt_qcow_cluster.o loop_file_fmt_qcow_cache.o -obj-$(CONFIG_BLK_DEV_LOOP_FILE_FMT_QCOW) += loop_file_fmt_qcow.o +loop_file_fmt_qcow-y += loop_file_fmt_qcow_main.o loop_file_fmt_qcow_cluster.o loop_file_fmt_qcow_cache.o +obj-$(CONFIG_BLK_DEV_XLOOP_FILE_FMT_QCOW) += loop_file_fmt_qcow.o @@ -3,7 +3,7 @@ # Loop device driver configuration # -config BLK_DEV_LOOP +config BLK_DEV_XLOOP tristate "Loopback device support" ---help--- Saying Y here will allow you to use a regular file as a block @@ -46,9 +46,9 @@ config BLK_DEV_LOOP Most users will answer N here. -config BLK_DEV_LOOP_MIN_COUNT +config BLK_DEV_XLOOP_MIN_COUNT int "Number of loop devices to pre-create at init time" - depends on BLK_DEV_LOOP + depends on BLK_DEV_XLOOP default 8 help Static number of loop devices to be unconditionally pre-created @@ -65,7 +65,7 @@ config BLK_DEV_CRYPTOLOOP tristate "Cryptoloop Support" select CRYPTO select CRYPTO_CBC - depends on BLK_DEV_LOOP + depends on BLK_DEV_XLOOP ---help--- Say Y here if you want to be able to use the ciphers that are provided by the CryptoAPI as loop transformation. This might be @@ -76,16 +76,16 @@ config BLK_DEV_CRYPTOLOOP instead, which can be configured to be on-disk compatible with the cryptoloop device. -config BLK_DEV_LOOP_FILE_FMT_RAW +config BLK_DEV_XLOOP_FILE_FMT_RAW tristate "Loop device binary file format support" - depends on BLK_DEV_LOOP + depends on BLK_DEV_XLOOP ---help--- Say Y or M here if you want to enable the binary (RAW) file format support of the loop device module. -config BLK_DEV_LOOP_FILE_FMT_QCOW +config BLK_DEV_XLOOP_FILE_FMT_QCOW tristate "Loop device QCOW file format support" - depends on BLK_DEV_LOOP + depends on BLK_DEV_XLOOP select ZLIB_INFLATE select ZLIB_DEFLATE ---help--- diff --git a/loop_file_fmt.c b/loop_file_fmt.c index ff356f1..062ea0d 100644 --- a/loop_file_fmt.c +++ b/loop_file_fmt.c @@ -2,7 +2,7 @@ /* * loop_file_fmt.c * - * File format subsystem for the loop device module. + * File format subsystem for the xloop device module. * * Copyright (C) 2019 Manuel Bentele <development@manuel-bentele.de> */ @@ -13,316 +13,335 @@ #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] = { +static struct xloop_file_fmt_driver *xloop_file_fmt_drivers[MAX_XLO_FILE_FMT] = { NULL }; -int loop_file_fmt_register_driver(struct loop_file_fmt_driver *drv) +int xloop_file_fmt_register_driver(struct xloop_file_fmt_driver *drv) { int ret = 0; if (drv == NULL) return -EFAULT; - if (drv->file_fmt_type > MAX_LO_FILE_FMT) + if (drv->file_fmt_type > MAX_XLO_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_file_fmt: successfully registered file " + if (xloop_file_fmt_drivers[drv->file_fmt_type] == NULL) { + xloop_file_fmt_drivers[drv->file_fmt_type] = drv; + printk(KERN_INFO "xloop_file_fmt: successfully registered file " "format driver %s", drv->name); } else { - printk(KERN_WARNING "loop_file_fmt: driver for file format " + printk(KERN_WARNING "xloop_file_fmt: driver for file format " "already registered"); ret = -EBUSY; } return ret; } -EXPORT_SYMBOL(loop_file_fmt_register_driver); +EXPORT_SYMBOL(xloop_file_fmt_register_driver); -void loop_file_fmt_unregister_driver(struct loop_file_fmt_driver *drv) +void xloop_file_fmt_unregister_driver(struct xloop_file_fmt_driver *drv) { if (drv == NULL) return; - if (drv->file_fmt_type > MAX_LO_FILE_FMT) + if (drv->file_fmt_type > MAX_XLO_FILE_FMT) return; - loop_file_fmt_drivers[drv->file_fmt_type] = NULL; - printk(KERN_INFO "loop_file_fmt: successfully unregistered file " + xloop_file_fmt_drivers[drv->file_fmt_type] = NULL; + printk(KERN_INFO "xloop_file_fmt: successfully unregistered file " "format driver %s", drv->name); } -EXPORT_SYMBOL(loop_file_fmt_unregister_driver); +EXPORT_SYMBOL(xloop_file_fmt_unregister_driver); -struct loop_file_fmt *loop_file_fmt_alloc(void) +struct xloop_file_fmt *xloop_file_fmt_alloc(void) { - return kzalloc(sizeof(struct loop_file_fmt), GFP_KERNEL); + return kzalloc(sizeof(struct xloop_file_fmt), GFP_KERNEL); } -void loop_file_fmt_free(struct loop_file_fmt *lo_fmt) +void xloop_file_fmt_free(struct xloop_file_fmt *xlo_fmt) { - kfree(lo_fmt); + kfree(xlo_fmt); } -int loop_file_fmt_set_lo(struct loop_file_fmt *lo_fmt, struct loop_device *lo) +int xloop_file_fmt_set_xlo(struct xloop_file_fmt *xlo_fmt, struct xloop_device *xlo) { - if (lo_fmt == NULL) + if (xlo_fmt == NULL) return -EINVAL; - lo_fmt->lo = lo; + xlo_fmt->xlo = xlo; return 0; } -EXPORT_SYMBOL(loop_file_fmt_set_lo); +EXPORT_SYMBOL(xloop_file_fmt_set_xlo); -struct loop_device *loop_file_fmt_get_lo(struct loop_file_fmt *lo_fmt) +struct xloop_device *xloop_file_fmt_get_xlo(struct xloop_file_fmt *xlo_fmt) { - return lo_fmt->lo; + return xlo_fmt->xlo; } -EXPORT_SYMBOL(loop_file_fmt_get_lo); +EXPORT_SYMBOL(xloop_file_fmt_get_xlo); -int loop_file_fmt_init(struct loop_file_fmt *lo_fmt, +int xloop_file_fmt_init(struct xloop_file_fmt *xlo_fmt, u32 file_fmt_type) { - struct loop_file_fmt_ops *ops; + struct xloop_file_fmt_ops *ops; struct module *drv; int ret = 0; - if (file_fmt_type > MAX_LO_FILE_FMT) + if (file_fmt_type > MAX_XLO_FILE_FMT) return -EINVAL; - lo_fmt->file_fmt_type = file_fmt_type; + xlo_fmt->file_fmt_type = file_fmt_type; - if (lo_fmt->file_fmt_state != file_fmt_uninitialized) { - printk(KERN_WARNING "loop_file_fmt: file format is " + if (xlo_fmt->file_fmt_state != file_fmt_uninitialized) { + printk(KERN_WARNING "xloop_file_fmt: file format is " "initialized already"); 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 " + if (xloop_file_fmt_drivers[xlo_fmt->file_fmt_type] == NULL) { + printk(KERN_ERR "xloop_file_fmt: file format driver is not " "available"); return -ENODEV; } - printk(KERN_INFO "loop_file_fmt: use file format driver %s", - loop_file_fmt_drivers[lo_fmt->file_fmt_type]->name); + printk(KERN_INFO "xloop_file_fmt: use file format driver %s", + xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->name); - drv = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->owner; + drv = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->owner; if (!try_module_get(drv)) { - printk(KERN_ERR "loop_file_fmt: file format driver %s can not " + printk(KERN_ERR "xloop_file_fmt: file format driver %s can not " "be accessed", - loop_file_fmt_drivers[lo_fmt->file_fmt_type]->name); + xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->name); return -ENODEV; } - ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops; + ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops; if (likely(ops->init)) { - ret = ops->init(lo_fmt); + ret = ops->init(xlo_fmt); if (ret < 0) goto free_drv; } /* after increasing the refcount of file format driver module and * the successful initialization, the file format is initialized */ - lo_fmt->file_fmt_state = file_fmt_initialized; + xlo_fmt->file_fmt_state = file_fmt_initialized; return ret; free_drv: module_put(drv); - lo_fmt->file_fmt_state = file_fmt_uninitialized; + xlo_fmt->file_fmt_state = file_fmt_uninitialized; return ret; } -void loop_file_fmt_exit(struct loop_file_fmt *lo_fmt) +void xloop_file_fmt_exit(struct xloop_file_fmt *xlo_fmt) { - struct loop_file_fmt_ops *ops; + struct xloop_file_fmt_ops *ops; struct module *drv; - if (lo_fmt->file_fmt_state != file_fmt_initialized) { - printk(KERN_WARNING "loop_file_fmt: file format is " + if (xlo_fmt->file_fmt_state != file_fmt_initialized) { + printk(KERN_WARNING "xloop_file_fmt: file format is " "uninitialized already"); return; } - ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops; + ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops; if (likely(ops->exit)) - ops->exit(lo_fmt); + ops->exit(xlo_fmt); - drv = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->owner; + drv = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->owner; module_put(drv); /* after decreasing the refcount of file format driver module, * the file format is uninitialized */ - lo_fmt->file_fmt_state = file_fmt_uninitialized; + xlo_fmt->file_fmt_state = file_fmt_uninitialized; } -int loop_file_fmt_read(struct loop_file_fmt *lo_fmt, +int xloop_file_fmt_read(struct xloop_file_fmt *xlo_fmt, struct request *rq) { - struct loop_file_fmt_ops *ops; + struct xloop_file_fmt_ops *ops; - if (unlikely(lo_fmt->file_fmt_state != file_fmt_initialized)) { - printk(KERN_ERR "loop_file_fmt: file format is " + if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) { + printk(KERN_ERR "xloop_file_fmt: file format is " "not initialized, can not read"); return -EINVAL; } - ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops; + ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops; if (likely(ops->read)) - return ops->read(lo_fmt, rq); + return ops->read(xlo_fmt, rq); else return -EIO; } -int loop_file_fmt_read_aio(struct loop_file_fmt *lo_fmt, +int xloop_file_fmt_read_aio(struct xloop_file_fmt *xlo_fmt, struct request *rq) { - struct loop_file_fmt_ops *ops; + struct xloop_file_fmt_ops *ops; - if (unlikely(lo_fmt->file_fmt_state != file_fmt_initialized)) { - printk(KERN_ERR "loop_file_fmt: file format is " + if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) { + printk(KERN_ERR "xloop_file_fmt: file format is " "not initialized, can not read aio"); return -EINVAL; } - ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops; + ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops; if (likely(ops->read_aio)) - return ops->read_aio(lo_fmt, rq); + return ops->read_aio(xlo_fmt, rq); else return -EIO; } -int loop_file_fmt_write(struct loop_file_fmt *lo_fmt, +int xloop_file_fmt_write(struct xloop_file_fmt *xlo_fmt, struct request *rq) { - struct loop_file_fmt_ops *ops; + struct xloop_file_fmt_ops *ops; - if (unlikely(lo_fmt->file_fmt_state != file_fmt_initialized)) { - printk(KERN_ERR "loop_file_fmt: file format is " + if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) { + printk(KERN_ERR "xloop_file_fmt: file format is " "not initialized, can not write"); return -EINVAL; } - ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops; + ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops; if (likely(ops->write)) - return ops->write(lo_fmt, rq); + return ops->write(xlo_fmt, rq); else return -EIO; } -int loop_file_fmt_write_aio(struct loop_file_fmt *lo_fmt, +int xloop_file_fmt_write_aio(struct xloop_file_fmt *xlo_fmt, struct request *rq) { - struct loop_file_fmt_ops *ops; + struct xloop_file_fmt_ops *ops; - if (unlikely(lo_fmt->file_fmt_state != file_fmt_initialized)) { - printk(KERN_ERR "loop_file_fmt: file format is " + if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) { + printk(KERN_ERR "xloop_file_fmt: file format is " "not initialized, can not write aio"); return -EINVAL; } - ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops; + ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops; if (likely(ops->write_aio)) - return ops->write_aio(lo_fmt, rq); + return ops->write_aio(xlo_fmt, rq); else return -EIO; } -int loop_file_fmt_discard(struct loop_file_fmt *lo_fmt, +int xloop_file_fmt_write_zeros(struct xloop_file_fmt *xlo_fmt, struct request *rq) { - struct loop_file_fmt_ops *ops; + struct xloop_file_fmt_ops *ops; - if (unlikely(lo_fmt->file_fmt_state != file_fmt_initialized)) { - printk(KERN_ERR "loop_file_fmt: file format is " + if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) { + printk(KERN_ERR "xloop_file_fmt: file format is " + "not initialized, can not write zeros"); + return -EINVAL; + } + + ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops; + if (likely(ops->write_zeros)) + return ops->write_zeros(xlo_fmt, rq); + else + return -EIO; +} + +int xloop_file_fmt_discard(struct xloop_file_fmt *xlo_fmt, + struct request *rq) +{ + struct xloop_file_fmt_ops *ops; + + if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) { + printk(KERN_ERR "xloop_file_fmt: file format is " "not initialized, can not discard"); return -EINVAL; } - ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops; + ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops; if (likely(ops->discard)) - return ops->discard(lo_fmt, rq); + return ops->discard(xlo_fmt, rq); else return -EIO; } -int loop_file_fmt_flush(struct loop_file_fmt *lo_fmt) +int xloop_file_fmt_flush(struct xloop_file_fmt *xlo_fmt) { - struct loop_file_fmt_ops *ops; + struct xloop_file_fmt_ops *ops; - if (unlikely(lo_fmt->file_fmt_state != file_fmt_initialized)) { - printk(KERN_ERR "loop_file_fmt: file format is " + if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) { + printk(KERN_ERR "xloop_file_fmt: file format is " "not initialized, can not flush"); return -EINVAL; } - ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops; + ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops; if (likely(ops->flush)) - return ops->flush(lo_fmt); + return ops->flush(xlo_fmt); return 0; } -loff_t loop_file_fmt_sector_size(struct loop_file_fmt *lo_fmt) +loff_t xloop_file_fmt_sector_size(struct xloop_file_fmt *xlo_fmt, + struct file *file, loff_t offset, loff_t sizelimit) { - struct loop_file_fmt_ops *ops; + struct xloop_file_fmt_ops *ops; - if (unlikely(lo_fmt->file_fmt_state != file_fmt_initialized)) { - printk(KERN_ERR "loop_file_fmt: file format is " + if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) { + printk(KERN_ERR "xloop_file_fmt: file format is " "not initialized, can not read sector size"); return 0; } - ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops; + ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops; if (likely(ops->sector_size)) - return ops->sector_size(lo_fmt); + return ops->sector_size(xlo_fmt, file, offset, sizelimit); else return 0; } -int loop_file_fmt_change(struct loop_file_fmt *lo_fmt, +int xloop_file_fmt_change(struct xloop_file_fmt *xlo_fmt, u32 file_fmt_type_new) { - if (file_fmt_type_new > MAX_LO_FILE_FMT) + if (file_fmt_type_new > MAX_XLO_FILE_FMT) return -EINVAL; /* Unload the old file format driver if the file format is * initialized */ - if (lo_fmt->file_fmt_state == file_fmt_initialized) - loop_file_fmt_exit(lo_fmt); + if (xlo_fmt->file_fmt_state == file_fmt_initialized) + xloop_file_fmt_exit(xlo_fmt); /* Load the new file format driver because the file format is * uninitialized now */ - return loop_file_fmt_init(lo_fmt, file_fmt_type_new); + return xloop_file_fmt_init(xlo_fmt, file_fmt_type_new); } -ssize_t loop_file_fmt_print_type(u32 file_fmt_type, char *file_fmt_name) +ssize_t xloop_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: + case XLO_FILE_FMT_RAW: len = sprintf(file_fmt_name, "%s", "RAW"); break; - case LO_FILE_FMT_QCOW: + case XLO_FILE_FMT_QCOW: len = sprintf(file_fmt_name, "%s", "QCOW"); break; - case LO_FILE_FMT_VDI: + case XLO_FILE_FMT_VDI: len = sprintf(file_fmt_name, "%s", "VDI"); break; - case LO_FILE_FMT_VMDK: + case XLO_FILE_FMT_VMDK: len = sprintf(file_fmt_name, "%s", "VMDK"); break; default: - len = sprintf(file_fmt_name, "%s", "ERROR: Unsupported loop " + len = sprintf(file_fmt_name, "%s", "ERROR: Unsupported xloop " "file format!"); break; } return len; } -EXPORT_SYMBOL(loop_file_fmt_print_type); +EXPORT_SYMBOL(xloop_file_fmt_print_type); diff --git a/loop_file_fmt.h b/loop_file_fmt.h index 8a16c86..38d6a3b 100644 --- a/loop_file_fmt.h +++ b/loop_file_fmt.h @@ -2,102 +2,109 @@ /* * loop_file_fmt.h * - * File format subsystem for the loop device module. + * File format subsystem for the xloop device module. * * Copyright (C) 2019 Manuel Bentele <development@manuel-bentele.de> */ -#ifndef _LINUX_LOOP_FILE_FMT_H -#define _LINUX_LOOP_FILE_FMT_H +#ifndef _LINUX_XLOOP_FILE_FMT_H +#define _LINUX_XLOOP_FILE_FMT_H #include "loop_main.h" -struct loop_file_fmt; +struct xloop_file_fmt; -#define LO_FILE_FMT_RAW 0 -#define LO_FILE_FMT_QCOW 1 -#define LO_FILE_FMT_VDI 2 -#define LO_FILE_FMT_VMDK 3 -#define MAX_LO_FILE_FMT (LO_FILE_FMT_VMDK + 1) +#define XLO_FILE_FMT_RAW 0 +#define XLO_FILE_FMT_QCOW 1 +#define XLO_FILE_FMT_VDI 2 +#define XLO_FILE_FMT_VMDK 3 +#define MAX_XLO_FILE_FMT (XLO_FILE_FMT_VMDK + 1) /** - * struct loop_file_fmt_ops - File format subsystem operations + * struct xloop_file_fmt_ops - File format subsystem operations * * Data structure representing the file format subsystem interface. */ -struct loop_file_fmt_ops { +struct xloop_file_fmt_ops { /** * @init: Initialization callback function */ - int (*init) (struct loop_file_fmt *lo_fmt); + int (*init) (struct xloop_file_fmt *xlo_fmt); /** * @exit: Release callback function */ - void (*exit) (struct loop_file_fmt *lo_fmt); + void (*exit) (struct xloop_file_fmt *xlo_fmt); /** * @read: Read IO callback function */ - int (*read) (struct loop_file_fmt *lo_fmt, - struct request *rq); + int (*read) (struct xloop_file_fmt *xlo_fmt, + struct request *rq); /** * @write: Write IO callback function */ - int (*write) (struct loop_file_fmt *lo_fmt, - struct request *rq); + int (*write) (struct xloop_file_fmt *xlo_fmt, + struct request *rq); /** * @read_aio: Asynchronous read IO callback function */ - int (*read_aio) (struct loop_file_fmt *lo_fmt, - struct request *rq); + int (*read_aio) (struct xloop_file_fmt *xlo_fmt, + struct request *rq); /** * @write_aio: Asynchronous write IO callback function */ - int (*write_aio) (struct loop_file_fmt *lo_fmt, - struct request *rq); + int (*write_aio) (struct xloop_file_fmt *xlo_fmt, + struct request *rq); + + /** + * @zero: Zero (discard) IO callback function + */ + int (*write_zeros) (struct xloop_file_fmt *xlo_fmt, + struct request *rq); /** * @discard: Discard IO callback function */ - int (*discard) (struct loop_file_fmt *lo_fmt, + int (*discard) (struct xloop_file_fmt *xlo_fmt, struct request *rq); /** * @flush: Flush callback function */ - int (*flush) (struct loop_file_fmt *lo_fmt); + int (*flush) (struct xloop_file_fmt *xlo_fmt); /** * @sector_size: Get sector size callback function */ - loff_t (*sector_size) (struct loop_file_fmt *lo_fmt); + loff_t (*sector_size) (struct xloop_file_fmt *xlo_fmt, + struct file *file, loff_t offset, loff_t sizelimit); }; /** - * struct loop_file_fmt_driver - File format subsystem driver + * struct xloop_file_fmt_driver - File format subsystem driver * * Data structure to implement file format drivers for the file format * subsystem. */ -struct loop_file_fmt_driver { +struct xloop_file_fmt_driver { /** * @name: Name of the file format driver */ const char *name; /** - * @file_fmt_type: Loop file format type of the file format driver + * @file_fmt_type: xloop file format type of the file format driver */ const u32 file_fmt_type; /** * @ops: Driver's implemented file format operations */ - struct loop_file_fmt_ops *ops; + struct xloop_file_fmt_ops *ops; /** * @ops: Owner of the file format driver @@ -109,26 +116,26 @@ struct loop_file_fmt_driver { * states of the file format * * transitions: - * loop_file_fmt_init(...) + * xloop_file_fmt_init(...) * ---> uninitialized ------------------------------> initialized - * loop_file_fmt_exit(...) + * xloop_file_fmt_exit(...) * initialized ------------------------------> uninitialized - * loop_file_fmt_read(...) + * xloop_file_fmt_read(...) * initialized ------------------------------> initialized - * loop_file_fmt_read_aio(...) + * xloop_file_fmt_read_aio(...) * initialized ------------------------------> initialized - * loop_file_fmt_write(...) + * xloop_file_fmt_write(...) * initialized ------------------------------> initialized - * loop_file_fmt_write_aio(...) + * xloop_file_fmt_write_aio(...) * initialized ------------------------------> initialized - * loop_file_fmt_discard(...) + * xloop_file_fmt_discard(...) * initialized ------------------------------> initialized - * loop_file_fmt_flush(...) + * xloop_file_fmt_flush(...) * initialized ------------------------------> initialized - * loop_file_fmt_sector_size(...) + * xloop_file_fmt_sector_size(...) * initialized ------------------------------> initialized * - * loop_file_fmt_change(...) + * xloop_file_fmt_change(...) * +-----------------------------------------------------------+ * | exit(...) init(...) | * | initialized -------> uninitialized -------> initialized | @@ -140,25 +147,25 @@ enum { }; /** - * struct loop_file_fmt - Loop file format + * struct xloop_file_fmt - xloop file format * - * Data structure to use with the file format the loop file format subsystem. + * Data structure to use with the file format the xloop file format subsystem. */ -struct loop_file_fmt { +struct xloop_file_fmt { /** - * @file_fmt_type: Current type of the loop file format + * @file_fmt_type: Current type of the xloop file format */ u32 file_fmt_type; /** - * @file_fmt_state: Current state of the loop file format + * @file_fmt_state: Current state of the xloop file format */ int file_fmt_state; /** - * @lo: Link to a file format's loop device + * @xlo: Link to a file format's xloop device */ - struct loop_device *lo; + struct xloop_device *xlo; /** * @private_data: Optional link to a file format's driver specific data @@ -170,188 +177,204 @@ struct loop_file_fmt { /* subsystem functions for the driver implementation */ /** - * loop_file_fmt_register_driver - Register a loop file format driver + * xloop_file_fmt_register_driver - Register a xloop file format driver * @drv: File format driver * - * Registers the specified loop file format driver @drv by the loop file format + * Registers the specified xloop file format driver @drv by the xloop file format * subsystem. */ -extern int loop_file_fmt_register_driver(struct loop_file_fmt_driver *drv); +extern int xloop_file_fmt_register_driver(struct xloop_file_fmt_driver *drv); /** - * loop_file_fmt_unregister_driver - Unregister a loop file format driver + * xloop_file_fmt_unregister_driver - Unregister a xloop file format driver * @drv: File format driver * - * Unregisters the specified loop file format driver @drv from the loop file + * Unregisters the specified xloop file format driver @drv from the xloop file * format subsystem. */ -extern void loop_file_fmt_unregister_driver(struct loop_file_fmt_driver *drv); +extern void xloop_file_fmt_unregister_driver(struct xloop_file_fmt_driver *drv); /* subsystem functions for subsystem usage */ /** - * loop_file_fmt_alloc - Allocate a loop file format + * xloop_file_fmt_alloc - Allocate a xloop file format * - * Dynamically allocates a loop file format and returns a pointer to the - * created loop file format. + * Dynamically allocates a xloop file format and returns a pointer to the + * created xloop file format. */ -extern struct loop_file_fmt *loop_file_fmt_alloc(void); +extern struct xloop_file_fmt *xloop_file_fmt_alloc(void); /** - * loop_file_fmt_free - Free an allocated loop file format - * @lo_fmt: Loop file format + * xloop_file_fmt_free - Free an allocated xloop file format + * @xlo_fmt: xloop file format * - * Frees the already allocated loop file format @lo_fmt. + * Frees the already allocated xloop file format @xlo_fmt. */ -extern void loop_file_fmt_free(struct loop_file_fmt *lo_fmt); +extern void xloop_file_fmt_free(struct xloop_file_fmt *xlo_fmt); /** - * loop_file_fmt_set_lo - Set the loop file format's loop device - * @lo_fmt: Loop file format - * @lo: Loop device + * xloop_file_fmt_set_xlo - Set the xloop file format's xloop device + * @xlo_fmt: xloop file format + * @xlo: xloop device * - * The link to the loop device @lo is set in the loop file format @lo_fmt. + * The link to the xloop device @xlo is set in the xloop file format @xlo_fmt. */ -extern int loop_file_fmt_set_lo(struct loop_file_fmt *lo_fmt, - struct loop_device *lo); +extern int xloop_file_fmt_set_xlo(struct xloop_file_fmt *xlo_fmt, + struct xloop_device *xlo); /** - * loop_file_fmt_get_lo - Get the loop file format's loop device - * @lo_fmt: Loop file format + * xloop_file_fmt_get_xlo - Get the xloop file format's xloop device + * @xlo_fmt: xloop file format * - * Returns a pointer to the loop device of the loop file format @lo_fmt. + * Returns a pointer to the xloop device of the xloop file format @xlo_fmt. */ -extern struct loop_device *loop_file_fmt_get_lo(struct loop_file_fmt *lo_fmt); +extern struct xloop_device *xloop_file_fmt_get_xlo(struct xloop_file_fmt *xlo_fmt); /** - * loop_file_fmt_init - Initialize a loop file format - * @lo_fmt: Loop file format + * xloop_file_fmt_init - Initialize a xloop file format + * @xlo_fmt: xloop file format * @file_fmt_type: Type of the file format * - * Initializes the specified loop file format @lo_fmt and sets up the correct + * Initializes the specified xloop file format @xlo_fmt and sets up the correct * file format type @file_fmt_type. Depending on @file_fmt_type, the correct - * loop file format driver is loaded in the subsystems backend. If no loop file + * xloop file format driver is loaded in the subsystems backend. If no xloop file * format driver for the specified file format is available an error is * returned. */ -extern int loop_file_fmt_init(struct loop_file_fmt *lo_fmt, +extern int xloop_file_fmt_init(struct xloop_file_fmt *xlo_fmt, u32 file_fmt_type); /** - * loop_file_fmt_exit - Release a loop file format - * @lo_fmt: Loop file format + * xloop_file_fmt_exit - Release a xloop file format + * @xlo_fmt: xloop file format * - * Releases the specified loop file format @lo_fmt and all its resources. + * Releases the specified xloop file format @xlo_fmt and all its resources. */ -extern void loop_file_fmt_exit(struct loop_file_fmt *lo_fmt); +extern void xloop_file_fmt_exit(struct xloop_file_fmt *xlo_fmt); /** - * loop_file_fmt_read - Read IO from a loop file format - * @lo_fmt: Loop file format + * xloop_file_fmt_read - Read IO from a xloop file format + * @xlo_fmt: xloop file format * @rq: IO Request * - * Reads IO from the file format's loop device by sending the IO read request - * @rq to the loop file format subsystem. The subsystem calls the registered - * callback function of the suitable loop file format driver. + * Reads IO from the file format's xloop device by sending the IO read request + * @rq to the xloop file format subsystem. The subsystem calls the registered + * callback function of the suitable xloop file format driver. */ -extern int loop_file_fmt_read(struct loop_file_fmt *lo_fmt, +extern int xloop_file_fmt_read(struct xloop_file_fmt *xlo_fmt, struct request *rq); /** - * loop_file_fmt_read_aio - Read IO from a loop file format asynchronously - * @lo_fmt: Loop file format + * xloop_file_fmt_read_aio - Read IO from a xloop file format asynchronously + * @xlo_fmt: xloop file format * @rq: IO Request * - * Reads IO from the file format's loop device asynchronously by sending the - * IO read aio request @rq to the loop file format subsystem. The subsystem - * calls the registered callback function of the suitable loop file format + * Reads IO from the file format's xloop device asynchronously by sending the + * IO read aio request @rq to the xloop file format subsystem. The subsystem + * calls the registered callback function of the suitable xloop file format * driver. */ -extern int loop_file_fmt_read_aio(struct loop_file_fmt *lo_fmt, +extern int xloop_file_fmt_read_aio(struct xloop_file_fmt *xlo_fmt, struct request *rq); /** - * loop_file_fmt_write - Write IO to a loop file format - * @lo_fmt: Loop file format + * xloop_file_fmt_write - Write IO to a xloop file format + * @xlo_fmt: xloop file format * @rq: IO Request * - * Write IO to the file format's loop device by sending the IO write request - * @rq to the loop file format subsystem. The subsystem calls the registered - * callback function of the suitable loop file format driver. + * Write IO to the file format's xloop device by sending the IO write request + * @rq to the xloop file format subsystem. The subsystem calls the registered + * callback function of the suitable xloop file format driver. */ -extern int loop_file_fmt_write(struct loop_file_fmt *lo_fmt, +extern int xloop_file_fmt_write(struct xloop_file_fmt *xlo_fmt, struct request *rq); /** - * loop_file_fmt_write_aio - Write IO to a loop file format asynchronously - * @lo_fmt: Loop file format + * xloop_file_fmt_write_aio - Write IO to a xloop file format asynchronously + * @xlo_fmt: xloop file format * @rq: IO Request * - * Write IO to the file format's loop device asynchronously by sending the - * IO write aio request @rq to the loop file format subsystem. The subsystem - * calls the registered callback function of the suitable loop file format + * Write IO to the file format's xloop device asynchronously by sending the + * IO write aio request @rq to the xloop file format subsystem. The subsystem + * calls the registered callback function of the suitable xloop file format * driver. */ -extern int loop_file_fmt_write_aio(struct loop_file_fmt *lo_fmt, +extern int xloop_file_fmt_write_aio(struct xloop_file_fmt *xlo_fmt, struct request *rq); /** - * loop_file_fmt_discard - Discard IO on a loop file format - * @lo_fmt: Loop file format + * xloop_file_fmt_write_zeros - Zero (discard) IO on a xloop file format + * @xlo_fmt: xloop file format + * @rq: IO Request + * + * Zero (discard) IO on the file format's xloop device by sending the IO write + * zeros request @rq to the xloop file format subsystem. The subsystem calls the + * registered callback function of the suitable xloop file format driver. + */ +extern int xloop_file_fmt_write_zeros(struct xloop_file_fmt *xlo_fmt, + struct request *rq); + +/** + * xloop_file_fmt_discard - Discard IO on a xloop file format + * @xlo_fmt: xloop file format * @rq: IO Request * - * Discard IO on the file format's loop device by sending the IO discard - * request @rq to the loop file format subsystem. The subsystem calls the - * registered callback function of the suitable loop file format driver. + * Discard IO on the file format's xloop device by sending the IO discard + * request @rq to the xloop file format subsystem. The subsystem calls the + * registered callback function of the suitable xloop file format driver. */ -extern int loop_file_fmt_discard(struct loop_file_fmt *lo_fmt, +extern int xloop_file_fmt_discard(struct xloop_file_fmt *xlo_fmt, struct request *rq); /** - * loop_file_fmt_flush - Flush a loop file format - * @lo_fmt: Loop file format + * xloop_file_fmt_flush - Flush a xloop file format + * @xlo_fmt: xloop file format * - * Flush the file format's loop device by calling the registered callback - * function of the suitable loop file format driver. + * Flush the file format's xloop device by calling the registered callback + * function of the suitable xloop file format driver. */ -extern int loop_file_fmt_flush(struct loop_file_fmt *lo_fmt); +extern int xloop_file_fmt_flush(struct xloop_file_fmt *xlo_fmt); /** - * loop_file_fmt_sector_size - Get sector size of a loop file format - * @lo_fmt: Loop file format + * xloop_file_fmt_sector_size - Get sector size of a xloop file format + * @xlo_fmt: xloop file format + * @file: xloop file formats file for sector size calculation + * @offset: Offset within the file for sector size calculation + * @sizelimit: Sizelimit of the file for sector size calculation * - * Returns the physical sector size of the loop file format's loop device. - * If the loop file format implements a sparse disk image format, then this + * Returns the physical sector size of the given xloop file format's file. + * If the xloop file format implements a sparse disk image format, then this * function returns the virtual sector size. */ -extern loff_t loop_file_fmt_sector_size(struct loop_file_fmt *lo_fmt); +extern loff_t xloop_file_fmt_sector_size(struct xloop_file_fmt *xlo_fmt, + struct file *file, loff_t offset, loff_t sizelimit); /** - * loop_file_fmt_change - Change the loop file format's type - * @lo_fmt: Loop file format - * @file_fmt_type_new: Loop file format type + * xloop_file_fmt_change - Change the xloop file format's type + * @xlo_fmt: xloop file format + * @file_fmt_type_new: xloop file format type * - * Changes the file format type of the already initialized loop file format - * @lo_fmt. Therefore, the function releases the old file format and frees all - * of its resources before the loop file format @lo_fmt is initialized and set + * Changes the file format type of the already initialized xloop file format + * @xlo_fmt. Therefore, the function releases the old file format and frees all + * of its resources before the xloop file format @xlo_fmt is initialized and set * up with the new file format @file_fmt_type_new. */ -extern int loop_file_fmt_change(struct loop_file_fmt *lo_fmt, +extern int xloop_file_fmt_change(struct xloop_file_fmt *xlo_fmt, u32 file_fmt_type_new); /* helper functions of the subsystem */ /** - * loop_file_fmt_print_type - Convert file format type to string - * @file_fmt_type: Loop file format type - * @file_fmt_name: Loop file format type string + * xloop_file_fmt_print_type - Convert file format type to string + * @file_fmt_type: xloop file format type + * @file_fmt_name: xloop file format type string * * Converts the specified numeric @file_fmt_type value into a human readable * string stating the file format as string in @file_fmt_name. */ -extern ssize_t loop_file_fmt_print_type(u32 file_fmt_type, +extern ssize_t xloop_file_fmt_print_type(u32 file_fmt_type, char *file_fmt_name); #endif diff --git a/loop_file_fmt_qcow_cache.c b/loop_file_fmt_qcow_cache.c index 7d3af73..4ef772a 100644 --- a/loop_file_fmt_qcow_cache.c +++ b/loop_file_fmt_qcow_cache.c @@ -2,7 +2,7 @@ /* * loop_file_fmt_qcow_cache.c * - * QCOW file format driver for the loop device module. + * QCOW file format driver for the xloop device module. * * Ported QCOW2 implementation of the QEMU project (GPL-2.0): * L2/refcount table cache for the QCOW2 format. @@ -23,14 +23,14 @@ #include "loop_file_fmt_qcow_main.h" #include "loop_file_fmt_qcow_cache.h" -static inline void *__loop_file_fmt_qcow_cache_get_table_addr( - struct loop_file_fmt_qcow_cache *c, int table) +static inline void *__xloop_file_fmt_qcow_cache_get_table_addr( + struct xloop_file_fmt_qcow_cache *c, int table) { return (u8 *) c->table_array + (size_t) table * c->table_size; } -static inline int __loop_file_fmt_qcow_cache_get_table_idx( - struct loop_file_fmt_qcow_cache *c, void *table) +static inline int __xloop_file_fmt_qcow_cache_get_table_idx( + struct xloop_file_fmt_qcow_cache *c, void *table) { ptrdiff_t table_offset = (u8 *) table - (u8 *) c->table_array; int idx = table_offset / c->table_size; @@ -38,10 +38,10 @@ static inline int __loop_file_fmt_qcow_cache_get_table_idx( return idx; } -static inline const char *__loop_file_fmt_qcow_cache_get_name( - struct loop_file_fmt *lo_fmt, struct loop_file_fmt_qcow_cache *c) +static inline const char *__xloop_file_fmt_qcow_cache_get_name( + struct xloop_file_fmt *xlo_fmt, struct xloop_file_fmt_qcow_cache *c) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; if (c == qcow_data->refcount_block_cache) { return "refcount block"; @@ -53,13 +53,13 @@ static inline const char *__loop_file_fmt_qcow_cache_get_name( } } -struct loop_file_fmt_qcow_cache *loop_file_fmt_qcow_cache_create( - struct loop_file_fmt *lo_fmt, int num_tables, unsigned table_size) +struct xloop_file_fmt_qcow_cache *xloop_file_fmt_qcow_cache_create( + struct xloop_file_fmt *xlo_fmt, int num_tables, unsigned table_size) { #ifdef CONFIG_DEBUG_DRIVER - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; #endif - struct loop_file_fmt_qcow_cache *c; + struct xloop_file_fmt_qcow_cache *c; ASSERT(num_tables > 0); ASSERT(is_power_of_2(table_size)); @@ -73,7 +73,7 @@ struct loop_file_fmt_qcow_cache *loop_file_fmt_qcow_cache_create( c->size = num_tables; c->table_size = table_size; - c->entries = vzalloc(sizeof(struct loop_file_fmt_qcow_cache_table) * + c->entries = vzalloc(sizeof(struct xloop_file_fmt_qcow_cache_table) * num_tables); c->table_array = vzalloc(num_tables * c->table_size); @@ -87,10 +87,10 @@ struct loop_file_fmt_qcow_cache *loop_file_fmt_qcow_cache_create( return c; } -void loop_file_fmt_qcow_cache_destroy(struct loop_file_fmt *lo_fmt) +void xloop_file_fmt_qcow_cache_destroy(struct xloop_file_fmt *xlo_fmt) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; - struct loop_file_fmt_qcow_cache *c = qcow_data->l2_table_cache; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; + struct xloop_file_fmt_qcow_cache *c = qcow_data->l2_table_cache; int i; for (i = 0; i < c->size; i++) { @@ -102,23 +102,23 @@ void loop_file_fmt_qcow_cache_destroy(struct loop_file_fmt *lo_fmt) kfree(c); } -static int __loop_file_fmt_qcow_cache_entry_flush( - struct loop_file_fmt_qcow_cache *c, int i) +static int __xloop_file_fmt_qcow_cache_entry_flush( + struct xloop_file_fmt_qcow_cache *c, int i) { if (!c->entries[i].dirty || !c->entries[i].offset) { return 0; } else { - printk(KERN_ERR "loop_file_fmt_qcow: Flush dirty cache tables " + printk(KERN_ERR "xloop_file_fmt_qcow: Flush dirty cache tables " "is not supported yet\n"); return -ENOSYS; } } -static int __loop_file_fmt_qcow_cache_do_get(struct loop_file_fmt *lo_fmt, - struct loop_file_fmt_qcow_cache *c, u64 offset, void **table, +static int __xloop_file_fmt_qcow_cache_do_get(struct xloop_file_fmt *xlo_fmt, + struct xloop_file_fmt_qcow_cache *c, u64 offset, void **table, bool read_from_disk) { - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); int i; int ret; int lookup_index; @@ -130,9 +130,9 @@ static int __loop_file_fmt_qcow_cache_do_get(struct loop_file_fmt *lo_fmt, ASSERT(offset != 0); if (!IS_ALIGNED(offset, c->table_size)) { - printk_ratelimited(KERN_ERR "loop_file_fmt_qcow: Cannot get " + printk_ratelimited(KERN_ERR "xloop_file_fmt_qcow: Cannot get " "entry from %s cache: offset %llx is unaligned\n", - __loop_file_fmt_qcow_cache_get_name(lo_fmt, c), + __xloop_file_fmt_qcow_cache_get_name(xlo_fmt, c), offset); return -EIO; } @@ -140,7 +140,7 @@ static int __loop_file_fmt_qcow_cache_do_get(struct loop_file_fmt *lo_fmt, /* Check if the table is already cached */ i = lookup_index = (offset / c->table_size * 4) % c->size; do { - const struct loop_file_fmt_qcow_cache_table *t = + const struct xloop_file_fmt_qcow_cache_table *t = &c->entries[i]; if (t->offset == offset) { goto found; @@ -164,7 +164,7 @@ static int __loop_file_fmt_qcow_cache_do_get(struct loop_file_fmt *lo_fmt, /* Cache miss: write a table back and replace it */ i = min_lru_index; - ret = __loop_file_fmt_qcow_cache_entry_flush(c, i); + ret = __xloop_file_fmt_qcow_cache_entry_flush(c, i); if (ret < 0) { return ret; } @@ -172,8 +172,8 @@ static int __loop_file_fmt_qcow_cache_do_get(struct loop_file_fmt *lo_fmt, c->entries[i].offset = 0; if (read_from_disk) { read_offset = offset; - len = kernel_read(lo->lo_backing_file, - __loop_file_fmt_qcow_cache_get_table_addr(c, i), + len = kernel_read(xlo->xlo_backing_file, + __xloop_file_fmt_qcow_cache_get_table_addr(c, i), c->table_size, &read_offset); if (len < 0) { len = ret; @@ -186,26 +186,26 @@ static int __loop_file_fmt_qcow_cache_do_get(struct loop_file_fmt *lo_fmt, /* And return the right table */ found: c->entries[i].ref++; - *table = __loop_file_fmt_qcow_cache_get_table_addr(c, i); + *table = __xloop_file_fmt_qcow_cache_get_table_addr(c, i); return 0; } -int loop_file_fmt_qcow_cache_get(struct loop_file_fmt *lo_fmt, u64 offset, +int xloop_file_fmt_qcow_cache_get(struct xloop_file_fmt *xlo_fmt, u64 offset, void **table) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; - struct loop_file_fmt_qcow_cache *c = qcow_data->l2_table_cache; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; + struct xloop_file_fmt_qcow_cache *c = qcow_data->l2_table_cache; - return __loop_file_fmt_qcow_cache_do_get(lo_fmt, c, offset, table, + return __xloop_file_fmt_qcow_cache_do_get(xlo_fmt, c, offset, table, true); } -void loop_file_fmt_qcow_cache_put(struct loop_file_fmt *lo_fmt, void **table) +void xloop_file_fmt_qcow_cache_put(struct xloop_file_fmt *xlo_fmt, void **table) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; - struct loop_file_fmt_qcow_cache *c = qcow_data->l2_table_cache; - int i = __loop_file_fmt_qcow_cache_get_table_idx(c, *table); + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; + struct xloop_file_fmt_qcow_cache *c = qcow_data->l2_table_cache; + int i = __xloop_file_fmt_qcow_cache_get_table_idx(c, *table); c->entries[i].ref--; *table = NULL; diff --git a/loop_file_fmt_qcow_cache.h b/loop_file_fmt_qcow_cache.h index 1abf9b2..d2f1010 100644 --- a/loop_file_fmt_qcow_cache.h +++ b/loop_file_fmt_qcow_cache.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * loop_file_fmt_qcow_cache.h + * xloop_file_fmt_qcow_cache.h * * Ported QCOW2 implementation of the QEMU project (GPL-2.0): * L2/refcount table cache for the QCOW2 format. @@ -11,21 +11,21 @@ * Copyright (C) 2019 Manuel Bentele <development@manuel-bentele.de> */ -#ifndef _LINUX_LOOP_FILE_FMT_QCOW_CACHE_H -#define _LINUX_LOOP_FILE_FMT_QCOW_CACHE_H +#ifndef _LINUX_XLOOP_FILE_FMT_QCOW_CACHE_H +#define _LINUX_XLOOP_FILE_FMT_QCOW_CACHE_H #include "loop_file_fmt.h" -struct loop_file_fmt_qcow_cache_table { +struct xloop_file_fmt_qcow_cache_table { s64 offset; u64 lru_counter; int ref; bool dirty; }; -struct loop_file_fmt_qcow_cache { - struct loop_file_fmt_qcow_cache_table *entries; - struct loop_file_fmt_qcow_cache *depends; +struct xloop_file_fmt_qcow_cache { + struct xloop_file_fmt_qcow_cache_table *entries; + struct xloop_file_fmt_qcow_cache *depends; int size; int table_size; bool depends_on_flush; @@ -34,18 +34,18 @@ struct loop_file_fmt_qcow_cache { u64 cache_clean_lru_counter; }; -extern struct loop_file_fmt_qcow_cache *loop_file_fmt_qcow_cache_create( - struct loop_file_fmt *lo_fmt, +extern struct xloop_file_fmt_qcow_cache *xloop_file_fmt_qcow_cache_create( + struct xloop_file_fmt *xlo_fmt, int num_tables, unsigned table_size); -extern void loop_file_fmt_qcow_cache_destroy(struct loop_file_fmt *lo_fmt); +extern void xloop_file_fmt_qcow_cache_destroy(struct xloop_file_fmt *xlo_fmt); -extern int loop_file_fmt_qcow_cache_get(struct loop_file_fmt *lo_fmt, +extern int xloop_file_fmt_qcow_cache_get(struct xloop_file_fmt *xlo_fmt, u64 offset, void **table); -extern void loop_file_fmt_qcow_cache_put(struct loop_file_fmt *lo_fmt, +extern void xloop_file_fmt_qcow_cache_put(struct xloop_file_fmt *xlo_fmt, void **table); #endif diff --git a/loop_file_fmt_qcow_cluster.c b/loop_file_fmt_qcow_cluster.c index 9c91a8b..593a173 100644 --- a/loop_file_fmt_qcow_cluster.c +++ b/loop_file_fmt_qcow_cluster.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * loop_file_fmt_qcow_cluster.c + * xloop_file_fmt_qcow_cluster.c * * Ported QCOW2 implementation of the QEMU project (GPL-2.0): * Cluster calculation and lookup for the QCOW2 format. @@ -24,18 +24,18 @@ * the cache is used; otherwise the L2 slice is loaded from the image * file. */ -static int __loop_file_fmt_qcow_cluster_l2_load(struct loop_file_fmt *lo_fmt, +static int __xloop_file_fmt_qcow_cluster_l2_load(struct xloop_file_fmt *xlo_fmt, u64 offset, u64 l2_offset, u64 **l2_slice) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; int start_of_slice = sizeof(u64) * ( - loop_file_fmt_qcow_offset_to_l2_index(qcow_data, offset) - - loop_file_fmt_qcow_offset_to_l2_slice_index(qcow_data, offset) + xloop_file_fmt_qcow_offset_to_l2_index(qcow_data, offset) - + xloop_file_fmt_qcow_offset_to_l2_slice_index(qcow_data, offset) ); ASSERT(qcow_data->l2_table_cache != NULL); - return loop_file_fmt_qcow_cache_get(lo_fmt, l2_offset + start_of_slice, + return xloop_file_fmt_qcow_cache_get(xlo_fmt, l2_offset + start_of_slice, (void **) l2_slice); } @@ -46,17 +46,17 @@ static int __loop_file_fmt_qcow_cluster_l2_load(struct loop_file_fmt *lo_fmt, * as contiguous. (This allows it, for example, to stop at the first compressed * cluster which may require a different handling) */ -static int __loop_file_fmt_qcow_cluster_count_contiguous( - struct loop_file_fmt *lo_fmt, int nb_clusters, int cluster_size, +static int __xloop_file_fmt_qcow_cluster_count_contiguous( + struct xloop_file_fmt *xlo_fmt, int nb_clusters, int cluster_size, u64 *l2_slice, u64 stop_flags) { int i; - enum loop_file_fmt_qcow_cluster_type first_cluster_type; + enum xloop_file_fmt_qcow_cluster_type first_cluster_type; u64 mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED; u64 first_entry = be64_to_cpu(l2_slice[0]); u64 offset = first_entry & mask; - first_cluster_type = loop_file_fmt_qcow_get_cluster_type(lo_fmt, + first_cluster_type = xloop_file_fmt_qcow_get_cluster_type(xlo_fmt, first_entry); if (first_cluster_type == QCOW_CLUSTER_UNALLOCATED) { return 0; @@ -80,9 +80,9 @@ static int __loop_file_fmt_qcow_cluster_count_contiguous( * Checks how many consecutive unallocated clusters in a given L2 * slice have the same cluster type. */ -static int __loop_file_fmt_qcow_cluster_count_contiguous_unallocated( - struct loop_file_fmt *lo_fmt, int nb_clusters, u64 *l2_slice, - enum loop_file_fmt_qcow_cluster_type wanted_type) +static int __xloop_file_fmt_qcow_cluster_count_contiguous_unallocated( + struct xloop_file_fmt *xlo_fmt, int nb_clusters, u64 *l2_slice, + enum xloop_file_fmt_qcow_cluster_type wanted_type) { int i; @@ -91,8 +91,8 @@ static int __loop_file_fmt_qcow_cluster_count_contiguous_unallocated( for (i = 0; i < nb_clusters; i++) { u64 entry = be64_to_cpu(l2_slice[i]); - enum loop_file_fmt_qcow_cluster_type type = - loop_file_fmt_qcow_get_cluster_type(lo_fmt, entry); + enum xloop_file_fmt_qcow_cluster_type type = + xloop_file_fmt_qcow_get_cluster_type(xlo_fmt, entry); if (type != wanted_type) { break; @@ -116,19 +116,19 @@ static int __loop_file_fmt_qcow_cluster_count_contiguous_unallocated( * Returns the cluster type (QCOW2_CLUSTER_*) on success, -errno in error * cases. */ -int loop_file_fmt_qcow_cluster_get_offset(struct loop_file_fmt *lo_fmt, +int xloop_file_fmt_qcow_cluster_get_offset(struct xloop_file_fmt *xlo_fmt, u64 offset, unsigned int *bytes, u64 *cluster_offset) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; unsigned int l2_index; u64 l1_index, l2_offset, *l2_slice; int c; unsigned int offset_in_cluster; u64 bytes_available, bytes_needed, nb_clusters; - enum loop_file_fmt_qcow_cluster_type type; + enum xloop_file_fmt_qcow_cluster_type type; int ret; - offset_in_cluster = loop_file_fmt_qcow_offset_into_cluster(qcow_data, + offset_in_cluster = xloop_file_fmt_qcow_offset_into_cluster(qcow_data, offset); bytes_needed = (u64) *bytes + offset_in_cluster; @@ -137,7 +137,7 @@ int loop_file_fmt_qcow_cluster_get_offset(struct loop_file_fmt *lo_fmt, * the entry pointing to it */ bytes_available = ((u64)( qcow_data->l2_slice_size - - loop_file_fmt_qcow_offset_to_l2_slice_index(qcow_data, offset)) + xloop_file_fmt_qcow_offset_to_l2_slice_index(qcow_data, offset)) ) << qcow_data->cluster_bits; if (bytes_needed > bytes_available) { @@ -147,7 +147,7 @@ int loop_file_fmt_qcow_cluster_get_offset(struct loop_file_fmt *lo_fmt, *cluster_offset = 0; /* seek to the l2 offset in the l1 table */ - l1_index = loop_file_fmt_qcow_offset_to_l1_index(qcow_data, offset); + l1_index = xloop_file_fmt_qcow_offset_to_l1_index(qcow_data, offset); if (l1_index >= qcow_data->l1_size) { type = QCOW_CLUSTER_UNALLOCATED; goto out; @@ -159,37 +159,37 @@ int loop_file_fmt_qcow_cluster_get_offset(struct loop_file_fmt *lo_fmt, goto out; } - if (loop_file_fmt_qcow_offset_into_cluster(qcow_data, l2_offset)) { - printk_ratelimited(KERN_ERR "loop_file_fmt_qcow: L2 table " + if (xloop_file_fmt_qcow_offset_into_cluster(qcow_data, l2_offset)) { + printk_ratelimited(KERN_ERR "xloop_file_fmt_qcow: L2 table " "offset %llx unaligned (L1 index: %llx)", l2_offset, l1_index); return -EIO; } /* load the l2 slice in memory */ - ret = __loop_file_fmt_qcow_cluster_l2_load(lo_fmt, offset, l2_offset, + ret = __xloop_file_fmt_qcow_cluster_l2_load(xlo_fmt, offset, l2_offset, &l2_slice); if (ret < 0) { return ret; } /* find the cluster offset for the given disk offset */ - l2_index = loop_file_fmt_qcow_offset_to_l2_slice_index(qcow_data, + l2_index = xloop_file_fmt_qcow_offset_to_l2_slice_index(qcow_data, offset); *cluster_offset = be64_to_cpu(l2_slice[l2_index]); - nb_clusters = loop_file_fmt_qcow_size_to_clusters(qcow_data, + nb_clusters = xloop_file_fmt_qcow_size_to_clusters(qcow_data, bytes_needed); /* bytes_needed <= *bytes + offset_in_cluster, both of which are * unsigned integers; the minimum cluster size is 512, so this * assertion is always true */ ASSERT(nb_clusters <= INT_MAX); - type = loop_file_fmt_qcow_get_cluster_type(lo_fmt, *cluster_offset); + type = xloop_file_fmt_qcow_get_cluster_type(xlo_fmt, *cluster_offset); if (qcow_data->qcow_version < 3 && ( type == QCOW_CLUSTER_ZERO_PLAIN || type == QCOW_CLUSTER_ZERO_ALLOC)) { - printk_ratelimited(KERN_ERR "loop_file_fmt_qcow: zero cluster " + printk_ratelimited(KERN_ERR "xloop_file_fmt_qcow: zero cluster " "entry found in pre-v3 image (L2 offset: %llx, " "L2 index: %x)\n", l2_offset, l2_index); ret = -EIO; @@ -197,8 +197,8 @@ int loop_file_fmt_qcow_cluster_get_offset(struct loop_file_fmt *lo_fmt, } switch (type) { case QCOW_CLUSTER_COMPRESSED: - if (loop_file_fmt_qcow_has_data_file(lo_fmt)) { - printk_ratelimited(KERN_ERR "loop_file_fmt_qcow: " + if (xloop_file_fmt_qcow_has_data_file(xlo_fmt)) { + printk_ratelimited(KERN_ERR "xloop_file_fmt_qcow: " "compressed cluster entry found in image with " "external data file (L2 offset: %llx, " "L2 index: %x)", l2_offset, l2_index); @@ -212,29 +212,29 @@ int loop_file_fmt_qcow_cluster_get_offset(struct loop_file_fmt *lo_fmt, case QCOW_CLUSTER_ZERO_PLAIN: case QCOW_CLUSTER_UNALLOCATED: /* how many empty clusters ? */ - c = __loop_file_fmt_qcow_cluster_count_contiguous_unallocated( - lo_fmt, nb_clusters, &l2_slice[l2_index], type); + c = __xloop_file_fmt_qcow_cluster_count_contiguous_unallocated( + xlo_fmt, nb_clusters, &l2_slice[l2_index], type); *cluster_offset = 0; break; case QCOW_CLUSTER_ZERO_ALLOC: case QCOW_CLUSTER_NORMAL: /* how many allocated clusters ? */ - c = __loop_file_fmt_qcow_cluster_count_contiguous(lo_fmt, + c = __xloop_file_fmt_qcow_cluster_count_contiguous(xlo_fmt, nb_clusters, qcow_data->cluster_size, &l2_slice[l2_index], QCOW_OFLAG_ZERO); *cluster_offset &= L2E_OFFSET_MASK; - if (loop_file_fmt_qcow_offset_into_cluster(qcow_data, + if (xloop_file_fmt_qcow_offset_into_cluster(qcow_data, *cluster_offset)) { - printk_ratelimited(KERN_ERR "loop_file_fmt_qcow: " + printk_ratelimited(KERN_ERR "xloop_file_fmt_qcow: " "cluster allocation offset %llx unaligned " "(L2 offset: %llx, L2 index: %x)\n", *cluster_offset, l2_offset, l2_index); ret = -EIO; goto fail; } - if (loop_file_fmt_qcow_has_data_file(lo_fmt) && + if (xloop_file_fmt_qcow_has_data_file(xlo_fmt) && *cluster_offset != offset - offset_in_cluster) { - printk_ratelimited(KERN_ERR "loop_file_fmt_qcow: " + printk_ratelimited(KERN_ERR "xloop_file_fmt_qcow: " "external data file host cluster offset %llx " "does not match guest cluster offset: %llx, " "L2 index: %x)", *cluster_offset, @@ -247,7 +247,7 @@ int loop_file_fmt_qcow_cluster_get_offset(struct loop_file_fmt *lo_fmt, BUG(); } - loop_file_fmt_qcow_cache_put(lo_fmt, (void **) &l2_slice); + xloop_file_fmt_qcow_cache_put(xlo_fmt, (void **) &l2_slice); bytes_available = (s64) c * qcow_data->cluster_size; @@ -265,6 +265,6 @@ out: return type; fail: - loop_file_fmt_qcow_cache_put(lo_fmt, (void **) &l2_slice); + xloop_file_fmt_qcow_cache_put(xlo_fmt, (void **) &l2_slice); return ret; } diff --git a/loop_file_fmt_qcow_cluster.h b/loop_file_fmt_qcow_cluster.h index d62e331..5078f29 100644 --- a/loop_file_fmt_qcow_cluster.h +++ b/loop_file_fmt_qcow_cluster.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * loop_file_fmt_qcow_cluster.h + * xloop_file_fmt_qcow_cluster.h * * Ported QCOW2 implementation of the QEMU project (GPL-2.0): * Cluster calculation and lookup for the QCOW2 format. @@ -10,12 +10,12 @@ * Copyright (C) 2019 Manuel Bentele <development@manuel-bentele.de> */ -#ifndef _LINUX_LOOP_FILE_FMT_QCOW_CLUSTER_H -#define _LINUX_LOOP_FILE_FMT_QCOW_CLUSTER_H +#ifndef _LINUX_XLOOP_FILE_FMT_QCOW_CLUSTER_H +#define _LINUX_XLOOP_FILE_FMT_QCOW_CLUSTER_H #include "loop_file_fmt.h" -extern int loop_file_fmt_qcow_cluster_get_offset(struct loop_file_fmt *lo_fmt, +extern int xloop_file_fmt_qcow_cluster_get_offset(struct xloop_file_fmt *xlo_fmt, u64 offset, unsigned int *bytes, u64 *cluster_offset); diff --git a/loop_file_fmt_qcow_main.c b/loop_file_fmt_qcow_main.c index 26f3e2d..7c3e360 100644 --- a/loop_file_fmt_qcow_main.c +++ b/loop_file_fmt_qcow_main.c @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * loop_file_fmt_qcow.c + * xloop_file_fmt_qcow.c * - * QCOW file format driver for the loop device module. + * QCOW file format driver for the xloop device module. * * Copyright (C) 2019 Manuel Bentele <development@manuel-bentele.de> */ @@ -27,20 +27,18 @@ #include "loop_file_fmt_qcow_cache.h" #include "loop_file_fmt_qcow_cluster.h" -static int __qcow_file_fmt_header_read(struct loop_file_fmt *lo_fmt, - struct loop_file_fmt_qcow_header *header) +static int __qcow_file_fmt_header_read(struct file *file, + struct xloop_file_fmt_qcow_header *header) { - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); ssize_t len; loff_t offset; int ret = 0; /* read QCOW header */ offset = 0; - len = kernel_read(lo->lo_backing_file, header, sizeof(*header), - &offset); + len = kernel_read(file, header, sizeof(*header), &offset); if (len < 0) { - printk(KERN_ERR "loop_file_fmt_qcow: could not read QCOW " + printk(KERN_ERR "xloop_file_fmt_qcow: could not read QCOW " "header"); return len; } @@ -63,13 +61,13 @@ static int __qcow_file_fmt_header_read(struct loop_file_fmt *lo_fmt, /* check QCOW file format and header version */ if (header->magic != QCOW_MAGIC) { - printk(KERN_ERR "loop_file_fmt_qcow: image is not in QCOW " + printk(KERN_ERR "xloop_file_fmt_qcow: image is not in QCOW " "format"); return -EINVAL; } if (header->version < 2 || header->version > 3) { - printk(KERN_ERR "loop_file_fmt_qcow: unsupported QCOW version " + printk(KERN_ERR "xloop_file_fmt_qcow: unsupported QCOW version " "%d", header->version); return -ENOTSUPP; } @@ -92,7 +90,7 @@ static int __qcow_file_fmt_header_read(struct loop_file_fmt *lo_fmt, header->header_length = be32_to_cpu(header->header_length); if (header->header_length < 104) { - printk(KERN_ERR "loop_file_fmt_qcow: QCOW header too " + printk(KERN_ERR "xloop_file_fmt_qcow: QCOW header too " "short"); return -EINVAL; } @@ -101,14 +99,14 @@ static int __qcow_file_fmt_header_read(struct loop_file_fmt *lo_fmt, return ret; } -static int __qcow_file_fmt_validate_table(struct loop_file_fmt *lo_fmt, +static int __qcow_file_fmt_validate_table(struct xloop_file_fmt *xlo_fmt, u64 offset, u64 entries, size_t entry_len, s64 max_size_bytes, const char *table_name) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; if (entries > max_size_bytes / entry_len) { - printk(KERN_INFO "loop_file_fmt_qcow: %s too large", + printk(KERN_INFO "xloop_file_fmt_qcow: %s too large", table_name); return -EFBIG; } @@ -116,9 +114,9 @@ static int __qcow_file_fmt_validate_table(struct loop_file_fmt *lo_fmt, /* Use signed S64_MAX as the maximum even for u64 header fields, * because values will be passed to qemu functions taking s64. */ if ((S64_MAX - entries * entry_len < offset) || ( - loop_file_fmt_qcow_offset_into_cluster(qcow_data, offset) != 0) + xloop_file_fmt_qcow_offset_into_cluster(qcow_data, offset) != 0) ) { - printk(KERN_INFO "loop_file_fmt_qcow: %s offset invalid", + printk(KERN_INFO "xloop_file_fmt_qcow: %s offset invalid", table_name); return -EINVAL; } @@ -126,16 +124,16 @@ static int __qcow_file_fmt_validate_table(struct loop_file_fmt *lo_fmt, return 0; } -static inline loff_t __qcow_file_fmt_rq_get_pos(struct loop_file_fmt *lo_fmt, +static inline loff_t __qcow_file_fmt_rq_get_pos(struct xloop_file_fmt *xlo_fmt, struct request *rq) { - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); - return ((loff_t) blk_rq_pos(rq) << 9) + lo->lo_offset; + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); + return ((loff_t) blk_rq_pos(rq) << 9) + xlo->xlo_offset; } -static int __qcow_file_fmt_compression_init(struct loop_file_fmt *lo_fmt) +static int __qcow_file_fmt_compression_init(struct xloop_file_fmt *xlo_fmt) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; int ret = 0; qcow_data->strm = kzalloc(sizeof(*qcow_data->strm), GFP_KERNEL); @@ -167,9 +165,9 @@ out: return ret; } -static void __qcow_file_fmt_compression_exit(struct loop_file_fmt *lo_fmt) +static void __qcow_file_fmt_compression_exit(struct xloop_file_fmt *xlo_fmt) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; vfree(qcow_data->strm->workspace); kfree(qcow_data->strm); @@ -177,10 +175,10 @@ static void __qcow_file_fmt_compression_exit(struct loop_file_fmt *lo_fmt) } #ifdef CONFIG_DEBUG_FS -static void __qcow_file_fmt_header_to_buf(struct loop_file_fmt *lo_fmt, - const struct loop_file_fmt_qcow_header *header) +static void __qcow_file_fmt_header_to_buf(struct xloop_file_fmt *xlo_fmt, + const struct xloop_file_fmt_qcow_header *header) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; char *header_buf = qcow_data->dbgfs_file_qcow_header_buf; ssize_t len = 0; @@ -235,8 +233,8 @@ static void __qcow_file_fmt_header_to_buf(struct loop_file_fmt *lo_fmt, static ssize_t __qcow_file_fmt_dbgfs_hdr_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { - struct loop_file_fmt *lo_fmt = file->private_data; - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt *xlo_fmt = file->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; char *header_buf = qcow_data->dbgfs_file_qcow_header_buf; return simple_read_from_buffer(buf, size, ppos, header_buf, @@ -251,8 +249,8 @@ static const struct file_operations qcow_file_fmt_dbgfs_hdr_fops = { static ssize_t __qcow_file_fmt_dbgfs_ofs_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { - struct loop_file_fmt *lo_fmt = file->private_data; - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt *xlo_fmt = file->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; unsigned int cur_bytes = 1; u64 offset = 0; u64 cluster_offset = 0; @@ -269,12 +267,12 @@ static ssize_t __qcow_file_fmt_dbgfs_ofs_read(struct file *file, mutex_unlock(&qcow_data->dbgfs_qcow_offset_mutex); /* calculate and print the cluster offset */ - ret = loop_file_fmt_qcow_cluster_get_offset(lo_fmt, + ret = xloop_file_fmt_qcow_cluster_get_offset(xlo_fmt, offset, &cur_bytes, &cluster_offset); if (ret < 0) return -EINVAL; - offset_in_cluster = loop_file_fmt_qcow_offset_into_cluster(qcow_data, + offset_in_cluster = xloop_file_fmt_qcow_offset_into_cluster(qcow_data, offset); len = sprintf(qcow_data->dbgfs_file_qcow_cluster_buf, @@ -290,8 +288,8 @@ static ssize_t __qcow_file_fmt_dbgfs_ofs_read(struct file *file, static ssize_t __qcow_file_fmt_dbgfs_ofs_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos) { - struct loop_file_fmt *lo_fmt = file->private_data; - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt *xlo_fmt = file->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; ssize_t len = 0; int ret = 0; @@ -326,20 +324,20 @@ static const struct file_operations qcow_file_fmt_dbgfs_ofs_fops = { .write = __qcow_file_fmt_dbgfs_ofs_write }; -static int __qcow_file_fmt_dbgfs_init(struct loop_file_fmt *lo_fmt) +static int __qcow_file_fmt_dbgfs_init(struct xloop_file_fmt *xlo_fmt) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); int ret = 0; - qcow_data->dbgfs_dir = debugfs_create_dir("QCOW", lo->lo_dbgfs_dir); + qcow_data->dbgfs_dir = debugfs_create_dir("QCOW", xlo->xlo_dbgfs_dir); if (IS_ERR_OR_NULL(qcow_data->dbgfs_dir)) { ret = -ENODEV; goto out; } qcow_data->dbgfs_file_qcow_header = debugfs_create_file("header", - S_IRUGO, qcow_data->dbgfs_dir, lo_fmt, + S_IRUGO, qcow_data->dbgfs_dir, xlo_fmt, &qcow_file_fmt_dbgfs_hdr_fops); if (IS_ERR_OR_NULL(qcow_data->dbgfs_file_qcow_header)) { ret = -ENODEV; @@ -347,7 +345,7 @@ static int __qcow_file_fmt_dbgfs_init(struct loop_file_fmt *lo_fmt) } qcow_data->dbgfs_file_qcow_offset = debugfs_create_file("offset", - S_IRUGO | S_IWUSR, qcow_data->dbgfs_dir, lo_fmt, + S_IRUGO | S_IWUSR, qcow_data->dbgfs_dir, xlo_fmt, &qcow_file_fmt_dbgfs_ofs_fops); if (IS_ERR_OR_NULL(qcow_data->dbgfs_file_qcow_offset)) { qcow_data->dbgfs_file_qcow_offset = NULL; @@ -370,9 +368,9 @@ out: return ret; } -static void __qcow_file_fmt_dbgfs_exit(struct loop_file_fmt *lo_fmt) +static void __qcow_file_fmt_dbgfs_exit(struct xloop_file_fmt *xlo_fmt) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; if (qcow_data->dbgfs_file_qcow_offset) debugfs_remove(qcow_data->dbgfs_file_qcow_offset); @@ -387,11 +385,11 @@ static void __qcow_file_fmt_dbgfs_exit(struct loop_file_fmt *lo_fmt) } #endif -static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) +static int qcow_file_fmt_init(struct xloop_file_fmt *xlo_fmt) { - struct loop_file_fmt_qcow_data *qcow_data; - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); - struct loop_file_fmt_qcow_header header; + struct xloop_file_fmt_qcow_data *qcow_data; + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); + struct xloop_file_fmt_qcow_header header; u64 l1_vm_state_index; u64 l2_cache_size; u64 l2_cache_entry_size; @@ -404,17 +402,17 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) if (!qcow_data) return -ENOMEM; - lo_fmt->private_data = qcow_data; + xlo_fmt->private_data = qcow_data; /* read the QCOW file header */ - ret = __qcow_file_fmt_header_read(lo_fmt, &header); + ret = __qcow_file_fmt_header_read(xlo->xlo_backing_file, &header); if (ret) goto free_qcow_data; /* save information of the header fields in human readable format in * a file buffer to access it with debugfs */ #ifdef CONFIG_DEBUG_FS - __qcow_file_fmt_header_to_buf(lo_fmt, &header); + __qcow_file_fmt_header_to_buf(xlo_fmt, &header); #endif qcow_data->qcow_version = header.version; @@ -422,7 +420,7 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) /* Initialise cluster size */ if (header.cluster_bits < QCOW_MIN_CLUSTER_BITS || header.cluster_bits > QCOW_MAX_CLUSTER_BITS) { - printk(KERN_ERR "loop_file_fmt_qcow: unsupported cluster " + printk(KERN_ERR "xloop_file_fmt_qcow: unsupported cluster " "size: 2^%d", header.cluster_bits); ret = -EINVAL; goto free_qcow_data; @@ -434,21 +432,21 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) (qcow_data->cluster_bits - SECTOR_SHIFT); if (header.header_length > qcow_data->cluster_size) { - printk(KERN_ERR "loop_file_fmt_qcow: QCOW header exceeds " + printk(KERN_ERR "xloop_file_fmt_qcow: QCOW header exceeds " "cluster size"); ret = -EINVAL; goto free_qcow_data; } if (header.backing_file_offset > qcow_data->cluster_size) { - printk(KERN_ERR "loop_file_fmt_qcow: invalid backing file " + printk(KERN_ERR "xloop_file_fmt_qcow: invalid backing file " "offset"); ret = -EINVAL; goto free_qcow_data; } if (header.backing_file_offset) { - printk(KERN_ERR "loop_file_fmt_qcow: backing file support not " + printk(KERN_ERR "xloop_file_fmt_qcow: backing file support not " "available"); ret = -ENOTSUPP; goto free_qcow_data; @@ -460,21 +458,21 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) qcow_data->autoclear_features = header.autoclear_features; if (qcow_data->incompatible_features & QCOW_INCOMPAT_DIRTY) { - printk(KERN_ERR "loop_file_fmt_qcow: image contains " + printk(KERN_ERR "xloop_file_fmt_qcow: image contains " "inconsistent refcounts"); ret = -EACCES; goto free_qcow_data; } if (qcow_data->incompatible_features & QCOW_INCOMPAT_CORRUPT) { - printk(KERN_ERR "loop_file_fmt_qcow: image is corrupt; cannot " + printk(KERN_ERR "xloop_file_fmt_qcow: image is corrupt; cannot " "be opened read/write"); ret = -EACCES; goto free_qcow_data; } if (qcow_data->incompatible_features & QCOW_INCOMPAT_DATA_FILE) { - printk(KERN_ERR "loop_file_fmt_qcow: clusters in the external " + printk(KERN_ERR "xloop_file_fmt_qcow: clusters in the external " "data file are not refcounted"); ret = -EACCES; goto free_qcow_data; @@ -482,7 +480,7 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) /* Check support for various header values */ if (header.refcount_order > 6) { - printk(KERN_ERR "loop_file_fmt_qcow: reference count entry " + printk(KERN_ERR "xloop_file_fmt_qcow: reference count entry " "width too large; may not exceed 64 bits"); ret = -EINVAL; goto free_qcow_data; @@ -494,7 +492,7 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) qcow_data->crypt_method_header = header.crypt_method; if (qcow_data->crypt_method_header) { - printk(KERN_ERR "loop_file_fmt_qcow: encryption support not " + printk(KERN_ERR "xloop_file_fmt_qcow: encryption support not " "available"); ret = -ENOTSUPP; goto free_qcow_data; @@ -517,13 +515,13 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) (qcow_data->cluster_bits - 3); if (header.refcount_table_clusters == 0) { - printk(KERN_ERR "loop_file_fmt_qcow: image does not contain a " + printk(KERN_ERR "xloop_file_fmt_qcow: image does not contain a " "reference count table"); ret = -EINVAL; goto free_qcow_data; } - ret = __qcow_file_fmt_validate_table(lo_fmt, + ret = __qcow_file_fmt_validate_table(xlo_fmt, qcow_data->refcount_table_offset, header.refcount_table_clusters, qcow_data->cluster_size, QCOW_MAX_REFTABLE_SIZE, "Reference count table"); @@ -535,17 +533,17 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) * qcow2_read_snapshots() because the size of each snapshot is * variable and we don't know it yet. * Here we only check the offset and number of snapshots. */ - ret = __qcow_file_fmt_validate_table(lo_fmt, header.snapshots_offset, + ret = __qcow_file_fmt_validate_table(xlo_fmt, header.snapshots_offset, header.nb_snapshots, - sizeof(struct loop_file_fmt_qcow_snapshot_header), - sizeof(struct loop_file_fmt_qcow_snapshot_header) * + sizeof(struct xloop_file_fmt_qcow_snapshot_header), + sizeof(struct xloop_file_fmt_qcow_snapshot_header) * QCOW_MAX_SNAPSHOTS, "Snapshot table"); if (ret < 0) { goto free_qcow_data; } /* read the level 1 table */ - ret = __qcow_file_fmt_validate_table(lo_fmt, header.l1_table_offset, + ret = __qcow_file_fmt_validate_table(xlo_fmt, header.l1_table_offset, header.l1_size, sizeof(u64), QCOW_MAX_L1_SIZE, "Active L1 table"); if (ret < 0) { @@ -554,10 +552,10 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) qcow_data->l1_size = header.l1_size; qcow_data->l1_table_offset = header.l1_table_offset; - l1_vm_state_index = loop_file_fmt_qcow_size_to_l1(qcow_data, + l1_vm_state_index = xloop_file_fmt_qcow_size_to_l1(qcow_data, header.size); if (l1_vm_state_index > INT_MAX) { - printk(KERN_ERR "loop_file_fmt_qcow: image is too big"); + printk(KERN_ERR "xloop_file_fmt_qcow: image is too big"); ret = -EFBIG; goto free_qcow_data; } @@ -566,7 +564,7 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) /* the L1 table must contain at least enough entries to put header.size * bytes */ if (qcow_data->l1_size < qcow_data->l1_vm_state_index) { - printk(KERN_ERR "loop_file_fmt_qcow: L1 table is too small"); + printk(KERN_ERR "xloop_file_fmt_qcow: L1 table is too small"); ret = -EINVAL; goto free_qcow_data; } @@ -575,16 +573,16 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) qcow_data->l1_table = vzalloc(round_up(qcow_data->l1_size * sizeof(u64), 512)); if (qcow_data->l1_table == NULL) { - printk(KERN_ERR "loop_file_fmt_qcow: could not " + printk(KERN_ERR "xloop_file_fmt_qcow: could not " "allocate L1 table"); ret = -ENOMEM; goto free_qcow_data; } - len = kernel_read(lo->lo_backing_file, qcow_data->l1_table, + len = kernel_read(xlo->xlo_backing_file, qcow_data->l1_table, qcow_data->l1_size * sizeof(u64), &qcow_data->l1_table_offset); if (len < 0) { - printk(KERN_ERR "loop_file_fmt_qcow: could not read L1 " + printk(KERN_ERR "xloop_file_fmt_qcow: could not read L1 " "table"); ret = len; goto free_l1_table; @@ -600,7 +598,7 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) qcow_data->nb_snapshots = header.nb_snapshots; if (qcow_data->nb_snapshots > 0) { - printk(KERN_ERR "loop_file_fmt_qcow: snapshots support not " + printk(KERN_ERR "xloop_file_fmt_qcow: snapshots support not " "available"); ret = -ENOTSUPP; goto free_l1_table; @@ -621,14 +619,14 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) } if (l2_cache_size > INT_MAX) { - printk(KERN_ERR "loop_file_fmt_qcow: L2 cache size too big"); + printk(KERN_ERR "xloop_file_fmt_qcow: L2 cache size too big"); ret = -EINVAL; goto free_l1_table; } qcow_data->l2_slice_size = l2_cache_entry_size / sizeof(u64); - qcow_data->l2_table_cache = loop_file_fmt_qcow_cache_create(lo_fmt, + qcow_data->l2_table_cache = xloop_file_fmt_qcow_cache_create(xlo_fmt, l2_cache_size, l2_cache_entry_size); if (!qcow_data->l2_table_cache) { ret = -ENOMEM; @@ -636,13 +634,13 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) } /* initialize compression support */ - ret = __qcow_file_fmt_compression_init(lo_fmt); + ret = __qcow_file_fmt_compression_init(xlo_fmt); if (ret < 0) goto free_l2_cache; /* initialize debugfs entries */ #ifdef CONFIG_DEBUG_FS - ret = __qcow_file_fmt_dbgfs_init(lo_fmt); + ret = __qcow_file_fmt_dbgfs_init(xlo_fmt); if (ret < 0) goto free_l2_cache; #endif @@ -650,46 +648,46 @@ static int qcow_file_fmt_init(struct loop_file_fmt *lo_fmt) return ret; free_l2_cache: - loop_file_fmt_qcow_cache_destroy(lo_fmt); + xloop_file_fmt_qcow_cache_destroy(xlo_fmt); free_l1_table: vfree(qcow_data->l1_table); free_qcow_data: kfree(qcow_data); - lo_fmt->private_data = NULL; + xlo_fmt->private_data = NULL; return ret; } -static void qcow_file_fmt_exit(struct loop_file_fmt *lo_fmt) +static void qcow_file_fmt_exit(struct xloop_file_fmt *xlo_fmt) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; #ifdef CONFIG_DEBUG_FS - __qcow_file_fmt_dbgfs_exit(lo_fmt); + __qcow_file_fmt_dbgfs_exit(xlo_fmt); #endif - __qcow_file_fmt_compression_exit(lo_fmt); + __qcow_file_fmt_compression_exit(xlo_fmt); if (qcow_data->l1_table) { vfree(qcow_data->l1_table); } if (qcow_data->l2_table_cache) { - loop_file_fmt_qcow_cache_destroy(lo_fmt); + xloop_file_fmt_qcow_cache_destroy(xlo_fmt); } if (qcow_data) { kfree(qcow_data); - lo_fmt->private_data = NULL; + xlo_fmt->private_data = NULL; } } -static ssize_t __qcow_file_fmt_buffer_decompress(struct loop_file_fmt *lo_fmt, +static ssize_t __qcow_file_fmt_buffer_decompress(struct xloop_file_fmt *xlo_fmt, void *dest, size_t dest_size, const void *src, size_t src_size) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; int ret = 0; qcow_data->strm->avail_in = src_size; @@ -716,22 +714,22 @@ static ssize_t __qcow_file_fmt_buffer_decompress(struct loop_file_fmt *lo_fmt, return ret; } -static int __qcow_file_fmt_read_compressed(struct loop_file_fmt *lo_fmt, +static int __qcow_file_fmt_read_compressed(struct xloop_file_fmt *xlo_fmt, struct bio_vec *bvec, u64 file_cluster_offset, u64 offset, u64 bytes, u64 bytes_done) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); int ret = 0, csize, nb_csectors; u64 coffset; u8 *in_buf = NULL; ssize_t len; void *data; unsigned long irq_flags; - int offset_in_cluster = loop_file_fmt_qcow_offset_into_cluster( + int offset_in_cluster = xloop_file_fmt_qcow_offset_into_cluster( qcow_data, offset); coffset = file_cluster_offset & qcow_data->cluster_offset_mask; @@ -748,14 +746,14 @@ static int __qcow_file_fmt_read_compressed(struct loop_file_fmt *lo_fmt, return -ENOMEM; } qcow_data->cmp_last_coffset = coffset; - len = kernel_read(lo->lo_backing_file, in_buf, csize, &coffset); + len = kernel_read(xlo->xlo_backing_file, in_buf, csize, &coffset); if (len < 0) { qcow_data->cmp_last_coffset = ULLONG_MAX; ret = len; goto out_free_in_buf; } - if (__qcow_file_fmt_buffer_decompress(lo_fmt, qcow_data->cmp_out_buf, + if (__qcow_file_fmt_buffer_decompress(xlo_fmt, qcow_data->cmp_out_buf, qcow_data->cluster_size, in_buf, csize) < 0) { qcow_data->cmp_last_coffset = ULLONG_MAX; ret = -EIO; @@ -775,12 +773,12 @@ out_free_in_buf: return ret; } -static int __qcow_file_fmt_read_bvec(struct loop_file_fmt *lo_fmt, +static int __qcow_file_fmt_read_bvec(struct xloop_file_fmt *xlo_fmt, struct bio_vec *bvec, loff_t *ppos) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); int offset_in_cluster; int ret; unsigned int cur_bytes; /* number of bytes in current iteration */ @@ -799,13 +797,13 @@ static int __qcow_file_fmt_read_bvec(struct loop_file_fmt *lo_fmt, /* prepare next request */ cur_bytes = bytes; - ret = loop_file_fmt_qcow_cluster_get_offset(lo_fmt, *ppos, + ret = xloop_file_fmt_qcow_cluster_get_offset(xlo_fmt, *ppos, &cur_bytes, &cluster_offset); if (ret < 0) { goto fail; } - offset_in_cluster = loop_file_fmt_qcow_offset_into_cluster( + offset_in_cluster = xloop_file_fmt_qcow_offset_into_cluster( qcow_data, *ppos); switch (ret) { @@ -819,7 +817,7 @@ static int __qcow_file_fmt_read_bvec(struct loop_file_fmt *lo_fmt, break; case QCOW_CLUSTER_COMPRESSED: - ret = __qcow_file_fmt_read_compressed(lo_fmt, bvec, + ret = __qcow_file_fmt_read_compressed(xlo_fmt, bvec, cluster_offset, *ppos, cur_bytes, bytes_done); if (ret < 0) { goto fail; @@ -836,7 +834,7 @@ static int __qcow_file_fmt_read_bvec(struct loop_file_fmt *lo_fmt, pos_read = cluster_offset + offset_in_cluster; data = bvec_kmap_irq(bvec, &irq_flags) + bytes_done; - len = kernel_read(lo->lo_backing_file, data, cur_bytes, + len = kernel_read(xlo->xlo_backing_file, data, cur_bytes, &pos_read); flush_dcache_page(bvec->bv_page); bvec_kunmap_irq(data, &irq_flags); @@ -862,7 +860,7 @@ fail: return ret; } -static int qcow_file_fmt_read(struct loop_file_fmt *lo_fmt, +static int qcow_file_fmt_read(struct xloop_file_fmt *xlo_fmt, struct request *rq) { struct bio_vec bvec; @@ -870,10 +868,10 @@ static int qcow_file_fmt_read(struct loop_file_fmt *lo_fmt, loff_t pos; int ret = 0; - pos = __qcow_file_fmt_rq_get_pos(lo_fmt, rq); + pos = __qcow_file_fmt_rq_get_pos(xlo_fmt, rq); rq_for_each_segment(bvec, rq, iter) { - ret = __qcow_file_fmt_read_bvec(lo_fmt, &bvec, &pos); + ret = __qcow_file_fmt_read_bvec(xlo_fmt, &bvec, &pos); if (ret) return ret; @@ -883,67 +881,73 @@ static int qcow_file_fmt_read(struct loop_file_fmt *lo_fmt, return ret; } -static loff_t qcow_file_fmt_sector_size(struct loop_file_fmt *lo_fmt) +static loff_t qcow_file_fmt_sector_size(struct xloop_file_fmt *xlo_fmt, + struct file *file, loff_t offset, loff_t sizelimit) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); - loff_t loopsize; + struct xloop_file_fmt_qcow_header header; + loff_t xloopsize; + int ret; - if (qcow_data->size > 0) - loopsize = qcow_data->size; - else + /* temporary read the QCOW file header of other QCOW image file */ + ret = __qcow_file_fmt_header_read(file, &header); + if (ret) return 0; - if (lo->lo_offset > 0) - loopsize -= lo->lo_offset; - - if (lo->lo_sizelimit > 0 && lo->lo_sizelimit < loopsize) - loopsize = lo->lo_sizelimit; + /* compute xloopsize in bytes */ + xloopsize = header.size; + if (offset > 0) + xloopsize -= offset; + /* offset is beyond i_size, weird but possible */ + if (xloopsize < 0) + return 0; + if (sizelimit > 0 && sizelimit < xloopsize) + xloopsize = sizelimit; /* * Unfortunately, if we want to do I/O on the device, * the number of 512-byte sectors has to fit into a sector_t. */ - return loopsize >> 9; + return xloopsize >> 9; } -static struct loop_file_fmt_ops qcow_file_fmt_ops = { +static struct xloop_file_fmt_ops qcow_file_fmt_ops = { .init = qcow_file_fmt_init, .exit = qcow_file_fmt_exit, .read = qcow_file_fmt_read, .write = NULL, .read_aio = NULL, .write_aio = NULL, + .write_zeros = NULL, .discard = NULL, .flush = NULL, - .sector_size = qcow_file_fmt_sector_size + .sector_size = qcow_file_fmt_sector_size, }; -static struct loop_file_fmt_driver qcow_file_fmt_driver = { +static struct xloop_file_fmt_driver qcow_file_fmt_driver = { .name = "QCOW", - .file_fmt_type = LO_FILE_FMT_QCOW, + .file_fmt_type = XLO_FILE_FMT_QCOW, .ops = &qcow_file_fmt_ops, - .owner = THIS_MODULE + .owner = THIS_MODULE, }; -static int __init loop_file_fmt_qcow_init(void) +static int __init xloop_file_fmt_qcow_init(void) { - printk(KERN_INFO "loop_file_fmt_qcow: init loop device QCOW file " + printk(KERN_INFO "xloop_file_fmt_qcow: init xloop device QCOW file " "format driver"); - return loop_file_fmt_register_driver(&qcow_file_fmt_driver); + return xloop_file_fmt_register_driver(&qcow_file_fmt_driver); } -static void __exit loop_file_fmt_qcow_exit(void) +static void __exit xloop_file_fmt_qcow_exit(void) { - printk(KERN_INFO "loop_file_fmt_qcow: exit loop device QCOW file " + printk(KERN_INFO "xloop_file_fmt_qcow: exit xloop device QCOW file " "format driver"); - loop_file_fmt_unregister_driver(&qcow_file_fmt_driver); + xloop_file_fmt_unregister_driver(&qcow_file_fmt_driver); } -module_init(loop_file_fmt_qcow_init); -module_exit(loop_file_fmt_qcow_exit); +module_init(xloop_file_fmt_qcow_init); +module_exit(xloop_file_fmt_qcow_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Manuel Bentele <development@manuel-bentele.de>"); -MODULE_DESCRIPTION("Loop device QCOW file format driver"); -MODULE_SOFTDEP("pre: loop"); +MODULE_DESCRIPTION("xloop device QCOW file format driver"); +MODULE_SOFTDEP("pre: xloop"); diff --git a/loop_file_fmt_qcow_main.h b/loop_file_fmt_qcow_main.h index dd6ed30..54b94c3 100644 --- a/loop_file_fmt_qcow_main.h +++ b/loop_file_fmt_qcow_main.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * loop_file_fmt_qcow.h + * xloop_file_fmt_qcow.h * - * QCOW file format driver for the loop device module. + * QCOW file format driver for the xloop device module. * * Ported QCOW2 implementation of the QEMU project (GPL-2.0): * Declarations for the QCOW2 file format. @@ -12,8 +12,8 @@ * Copyright (C) 2019 Manuel Bentele <development@manuel-bentele.de> */ -#ifndef _LINUX_LOOP_FILE_FMT_QCOW_H -#define _LINUX_LOOP_FILE_FMT_QCOW_H +#ifndef _LINUX_XLOOP_FILE_FMT_QCOW_H +#define _LINUX_XLOOP_FILE_FMT_QCOW_H #include <linux/list.h> #include <linux/mutex.h> @@ -106,7 +106,7 @@ do { \ #define QCOW_OFFSET_BUF_LEN 32 #define QCOW_CLUSTER_BUF_LEN 128 -struct loop_file_fmt_qcow_header { +struct xloop_file_fmt_qcow_header { u32 magic; u32 version; u64 backing_file_offset; @@ -130,7 +130,7 @@ struct loop_file_fmt_qcow_header { u32 header_length; } __attribute__((packed)); -struct loop_file_fmt_qcow_snapshot_header { +struct xloop_file_fmt_qcow_snapshot_header { /* header is 8 byte aligned */ u64 l1_table_offset; @@ -190,7 +190,7 @@ enum { QCOW_AUTOCLEAR_DATA_FILE_RAW, }; -struct loop_file_fmt_qcow_data { +struct xloop_file_fmt_qcow_data { u64 size; int cluster_bits; int cluster_size; @@ -208,8 +208,8 @@ struct loop_file_fmt_qcow_data { u64 l1_table_offset; u64 *l1_table; - struct loop_file_fmt_qcow_cache *l2_table_cache; - struct loop_file_fmt_qcow_cache *refcount_block_cache; + struct xloop_file_fmt_qcow_cache *l2_table_cache; + struct xloop_file_fmt_qcow_cache *refcount_block_cache; u64 *refcount_table; u64 refcount_table_offset; @@ -254,7 +254,7 @@ struct loop_file_fmt_qcow_data { #endif }; -struct loop_file_fmt_qcow_cow_region { +struct xloop_file_fmt_qcow_cow_region { /** * Offset of the COW region in bytes from the start of the first * cluster touched by the request. @@ -265,7 +265,7 @@ struct loop_file_fmt_qcow_cow_region { unsigned nb_bytes; }; -enum loop_file_fmt_qcow_cluster_type { +enum xloop_file_fmt_qcow_cluster_type { QCOW_CLUSTER_UNALLOCATED, QCOW_CLUSTER_ZERO_PLAIN, QCOW_CLUSTER_ZERO_ALLOC, @@ -273,7 +273,7 @@ enum loop_file_fmt_qcow_cluster_type { QCOW_CLUSTER_COMPRESSED, }; -enum loop_file_fmt_qcow_metadata_overlap { +enum xloop_file_fmt_qcow_metadata_overlap { QCOW_OL_MAIN_HEADER_BITNR = 0, QCOW_OL_ACTIVE_L1_BITNR = 1, QCOW_OL_ACTIVE_L2_BITNR = 2, @@ -322,75 +322,75 @@ enum loop_file_fmt_qcow_metadata_overlap { #define INV_OFFSET (-1ULL) -static inline bool loop_file_fmt_qcow_has_data_file( - struct loop_file_fmt *lo_fmt) +static inline bool xloop_file_fmt_qcow_has_data_file( + struct xloop_file_fmt *xlo_fmt) { /* At the moment, there is no support for copy on write! */ return false; } -static inline bool loop_file_fmt_qcow_data_file_is_raw( - struct loop_file_fmt *lo_fmt) +static inline bool xloop_file_fmt_qcow_data_file_is_raw( + struct xloop_file_fmt *xlo_fmt) { - struct loop_file_fmt_qcow_data *qcow_data = lo_fmt->private_data; + struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; return !!(qcow_data->autoclear_features & QCOW_AUTOCLEAR_DATA_FILE_RAW); } -static inline s64 loop_file_fmt_qcow_start_of_cluster( - struct loop_file_fmt_qcow_data *qcow_data, s64 offset) +static inline s64 xloop_file_fmt_qcow_start_of_cluster( + struct xloop_file_fmt_qcow_data *qcow_data, s64 offset) { return offset & ~(qcow_data->cluster_size - 1); } -static inline s64 loop_file_fmt_qcow_offset_into_cluster( - struct loop_file_fmt_qcow_data *qcow_data, s64 offset) +static inline s64 xloop_file_fmt_qcow_offset_into_cluster( + struct xloop_file_fmt_qcow_data *qcow_data, s64 offset) { return offset & (qcow_data->cluster_size - 1); } -static inline s64 loop_file_fmt_qcow_size_to_clusters( - struct loop_file_fmt_qcow_data *qcow_data, u64 size) +static inline s64 xloop_file_fmt_qcow_size_to_clusters( + struct xloop_file_fmt_qcow_data *qcow_data, u64 size) { return (size + (qcow_data->cluster_size - 1)) >> qcow_data->cluster_bits; } -static inline s64 loop_file_fmt_qcow_size_to_l1( - struct loop_file_fmt_qcow_data *qcow_data, s64 size) +static inline s64 xloop_file_fmt_qcow_size_to_l1( + struct xloop_file_fmt_qcow_data *qcow_data, s64 size) { int shift = qcow_data->cluster_bits + qcow_data->l2_bits; return (size + (1ULL << shift) - 1) >> shift; } -static inline int loop_file_fmt_qcow_offset_to_l1_index( - struct loop_file_fmt_qcow_data *qcow_data, u64 offset) +static inline int xloop_file_fmt_qcow_offset_to_l1_index( + struct xloop_file_fmt_qcow_data *qcow_data, u64 offset) { return offset >> (qcow_data->l2_bits + qcow_data->cluster_bits); } -static inline int loop_file_fmt_qcow_offset_to_l2_index( - struct loop_file_fmt_qcow_data *qcow_data, s64 offset) +static inline int xloop_file_fmt_qcow_offset_to_l2_index( + struct xloop_file_fmt_qcow_data *qcow_data, s64 offset) { return (offset >> qcow_data->cluster_bits) & (qcow_data->l2_size - 1); } -static inline int loop_file_fmt_qcow_offset_to_l2_slice_index( - struct loop_file_fmt_qcow_data *qcow_data, s64 offset) +static inline int xloop_file_fmt_qcow_offset_to_l2_slice_index( + struct xloop_file_fmt_qcow_data *qcow_data, s64 offset) { return (offset >> qcow_data->cluster_bits) & (qcow_data->l2_slice_size - 1); } -static inline s64 loop_file_fmt_qcow_vm_state_offset( - struct loop_file_fmt_qcow_data *qcow_data) +static inline s64 xloop_file_fmt_qcow_vm_state_offset( + struct xloop_file_fmt_qcow_data *qcow_data) { return (s64)qcow_data->l1_vm_state_index << (qcow_data->cluster_bits + qcow_data->l2_bits); } -static inline enum loop_file_fmt_qcow_cluster_type -loop_file_fmt_qcow_get_cluster_type(struct loop_file_fmt *lo_fmt, u64 l2_entry) +static inline enum xloop_file_fmt_qcow_cluster_type +xloop_file_fmt_qcow_get_cluster_type(struct xloop_file_fmt *xlo_fmt, u64 l2_entry) { if (l2_entry & QCOW_OFLAG_COMPRESSED) { return QCOW_CLUSTER_COMPRESSED; @@ -405,7 +405,7 @@ loop_file_fmt_qcow_get_cluster_type(struct loop_file_fmt *lo_fmt, u64 l2_entry) * However, all clusters in external data files always have * refcount 1, so we can rely on QCOW_OFLAG_COPIED to * disambiguate. */ - if (loop_file_fmt_qcow_has_data_file(lo_fmt) && + if (xloop_file_fmt_qcow_has_data_file(xlo_fmt) && (l2_entry & QCOW_OFLAG_COPIED)) { return QCOW_CLUSTER_NORMAL; } else { diff --git a/loop_file_fmt_raw.c b/loop_file_fmt_raw.c index baa5602..11cc8cd 100644 --- a/loop_file_fmt_raw.c +++ b/loop_file_fmt_raw.c @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * loop_file_fmt_raw.c + * xloop_file_fmt_raw.c * - * RAW file format driver for the loop device module. + * RAW file format driver for the xloop device module. * * Copyright (C) 2019 Manuel Bentele <development@manuel-bentele.de> */ @@ -19,58 +19,49 @@ #include <linux/sched.h> #include <linux/types.h> #include <linux/uio.h> +#include <linux/version.h> #include "loop_file_fmt.h" -static inline loff_t __raw_file_fmt_rq_get_pos(struct loop_file_fmt *lo_fmt, +static inline loff_t __raw_file_fmt_rq_get_pos(struct xloop_file_fmt *xlo_fmt, struct request *rq) { - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); - return ((loff_t) blk_rq_pos(rq) << 9) + lo->lo_offset; + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); + return ((loff_t) blk_rq_pos(rq) << 9) + xlo->xlo_offset; } -/* transfer function for DEPRECATED cryptoloop support */ -static inline int __raw_file_fmt_do_transfer(struct loop_file_fmt *lo_fmt, - int cmd, - struct page *rpage, - unsigned roffs, - struct page *lpage, - unsigned loffs, - int size, - sector_t rblock) +/* transfer function for DEPRECATED cryptoxloop support */ +static inline int __raw_file_fmt_do_transfer(struct xloop_device *xlo, int cmd, + struct page *rpage, unsigned roffs, + struct page *lpage, unsigned loffs, + int size, sector_t rblock) { - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); int ret; - ret = lo->transfer(lo, cmd, rpage, roffs, lpage, loffs, size, rblock); + ret = xlo->transfer(xlo, cmd, rpage, roffs, lpage, loffs, size, rblock); if (likely(!ret)) return 0; printk_ratelimited(KERN_ERR - "loop: Transfer error at byte offset %llu, length %i.\n", + "xloop_file_fmt_raw: Transfer error at byte offset %llu, length %i.\n", (unsigned long long)rblock << 9, size); return ret; } -static int raw_file_fmt_read_transfer(struct loop_file_fmt *lo_fmt, - struct request *rq) +static int __raw_file_fmt_read_transfer(struct xloop_device *xlo, + struct request *rq, loff_t pos) { struct bio_vec bvec, b; struct req_iterator iter; struct iov_iter i; struct page *page; - struct loop_device *lo; ssize_t len; int ret = 0; - loff_t pos; page = alloc_page(GFP_NOIO); if (unlikely(!page)) return -ENOMEM; - lo = loop_file_fmt_get_lo(lo_fmt); - pos = __raw_file_fmt_rq_get_pos(lo_fmt, rq); - rq_for_each_segment(bvec, rq, iter) { loff_t offset = pos; @@ -79,14 +70,14 @@ static int raw_file_fmt_read_transfer(struct loop_file_fmt *lo_fmt, b.bv_len = bvec.bv_len; iov_iter_bvec(&i, READ, &b, 1, b.bv_len); - len = vfs_iter_read(lo->lo_backing_file, &i, &pos, 0); + len = vfs_iter_read(xlo->xlo_backing_file, &i, &pos, 0); if (len < 0) { ret = len; goto out_free_page; } - ret = __raw_file_fmt_do_transfer(lo_fmt, READ, page, 0, - bvec.bv_page, bvec.bv_offset, len, offset >> 9); + ret = __raw_file_fmt_do_transfer(xlo, READ, page, 0, bvec.bv_page, + bvec.bv_offset, len, offset >> 9); if (ret) goto out_free_page; @@ -107,26 +98,25 @@ out_free_page: return ret; } -static int raw_file_fmt_read(struct loop_file_fmt *lo_fmt, +static int raw_file_fmt_read(struct xloop_file_fmt *xlo_fmt, struct request *rq) { struct bio_vec bvec; struct req_iterator iter; struct iov_iter i; - struct loop_device *lo; ssize_t len; + struct xloop_device *xlo; loff_t pos; - lo = loop_file_fmt_get_lo(lo_fmt); + xlo = xloop_file_fmt_get_xlo(xlo_fmt); + pos = __raw_file_fmt_rq_get_pos(xlo_fmt, rq); - if (lo->transfer) - return raw_file_fmt_read_transfer(lo_fmt, rq); - - pos = __raw_file_fmt_rq_get_pos(lo_fmt, rq); + if (xlo->transfer) + return __raw_file_fmt_read_transfer(xlo, rq, pos); rq_for_each_segment(bvec, rq, iter) { iov_iter_bvec(&i, READ, &bvec, 1, bvec.bv_len); - len = vfs_iter_read(lo->lo_backing_file, &i, &pos, 0); + len = vfs_iter_read(xlo->xlo_backing_file, &i, &pos, 0); if (len < 0) return len; @@ -145,7 +135,7 @@ static int raw_file_fmt_read(struct loop_file_fmt *lo_fmt, return 0; } -static void __raw_file_fmt_rw_aio_do_completion(struct loop_cmd *cmd) +static void __raw_file_fmt_rw_aio_do_completion(struct xloop_cmd *cmd) { struct request *rq = blk_mq_rq_from_pdu(cmd); @@ -153,12 +143,17 @@ static void __raw_file_fmt_rw_aio_do_completion(struct loop_cmd *cmd) return; kfree(cmd->bvec); cmd->bvec = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + if (likely(!blk_should_fake_timeout(rq->q))) + blk_mq_complete_request(rq); +#else blk_mq_complete_request(rq); +#endif } static void __raw_file_fmt_rw_aio_complete(struct kiocb *iocb, long ret, long ret2) { - struct loop_cmd *cmd = container_of(iocb, struct loop_cmd, iocb); + struct xloop_cmd *cmd = container_of(iocb, struct xloop_cmd, iocb); if (cmd->css) css_put(cmd->css); @@ -166,27 +161,19 @@ static void __raw_file_fmt_rw_aio_complete(struct kiocb *iocb, long ret, long re __raw_file_fmt_rw_aio_do_completion(cmd); } -static int __raw_file_fmt_rw_aio(struct loop_file_fmt *lo_fmt, - struct request *rq, - bool rw) +static int __raw_file_fmt_rw_aio(struct xloop_device *xlo, + struct xloop_cmd *cmd, loff_t pos, bool rw) { struct iov_iter iter; struct req_iterator rq_iter; struct bio_vec *bvec; + struct request *rq = blk_mq_rq_from_pdu(cmd); struct bio *bio = rq->bio; - struct file *file; + struct file *file = xlo->xlo_backing_file; struct bio_vec tmp; - struct loop_device *lo; - struct loop_cmd *cmd; unsigned int offset; int nr_bvec = 0; int ret; - loff_t pos; - - lo = loop_file_fmt_get_lo(lo_fmt); - file = lo->lo_backing_file; - cmd = blk_mq_rq_to_pdu(rq); - pos = __raw_file_fmt_rq_get_pos(lo_fmt, rq); rq_for_each_bvec(tmp, rq, rq_iter) nr_bvec++; @@ -246,10 +233,14 @@ static int __raw_file_fmt_rw_aio(struct loop_file_fmt *lo_fmt, return 0; } -static int raw_file_fmt_read_aio(struct loop_file_fmt *lo_fmt, +static int raw_file_fmt_read_aio(struct xloop_file_fmt *xlo_fmt, struct request *rq) { - return __raw_file_fmt_rw_aio(lo_fmt, rq, READ); + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); + struct xloop_cmd *cmd = blk_mq_rq_to_pdu(rq); + loff_t pos = __raw_file_fmt_rq_get_pos(xlo_fmt, rq); + + return __raw_file_fmt_rw_aio(xlo, cmd, pos, READ); } static int __raw_file_fmt_write_bvec(struct file *file, @@ -269,41 +260,40 @@ static int __raw_file_fmt_write_bvec(struct file *file, return 0; printk_ratelimited(KERN_ERR - "loop_file_fmt_raw: Write error at byte offset %llu, length " + "xloop_file_fmt_raw: Write error at byte offset %llu, length " "%i.\n", (unsigned long long)*ppos, bvec->bv_len); if (bw >= 0) bw = -EIO; return bw; } -static int raw_file_fmt_write_transfer(struct loop_file_fmt *lo_fmt, - struct request *rq) +/* + * This is the slow, transforming version that needs to double buffer the + * data as it cannot do the transformations in place without having direct + * access to the destination pages of the backing file. + */ +static int __raw_file_fmt_write_transfer(struct xloop_device *xlo, + struct request *rq, loff_t pos) { - struct bio_vec bvec, b; +struct bio_vec bvec, b; struct req_iterator iter; struct page *page; - struct loop_device *lo; int ret = 0; - loff_t pos; - - lo = loop_file_fmt_get_lo(lo_fmt); - pos = __raw_file_fmt_rq_get_pos(lo_fmt, rq); page = alloc_page(GFP_NOIO); if (unlikely(!page)) return -ENOMEM; rq_for_each_segment(bvec, rq, iter) { - ret = __raw_file_fmt_do_transfer(lo_fmt, WRITE, page, 0, - bvec.bv_page, bvec.bv_offset, bvec.bv_len, pos >> 9); + ret = __raw_file_fmt_do_transfer(xlo, WRITE, page, 0, bvec.bv_page, + bvec.bv_offset, bvec.bv_len, pos >> 9); if (unlikely(ret)) break; b.bv_page = page; b.bv_offset = 0; b.bv_len = bvec.bv_len; - ret = __raw_file_fmt_write_bvec(lo->lo_backing_file, &b, - &pos); + ret = __raw_file_fmt_write_bvec(xlo->xlo_backing_file, &b, &pos); if (ret < 0) break; } @@ -312,25 +302,23 @@ static int raw_file_fmt_write_transfer(struct loop_file_fmt *lo_fmt, return ret; } -static int raw_file_fmt_write(struct loop_file_fmt *lo_fmt, +static int raw_file_fmt_write(struct xloop_file_fmt *xlo_fmt, struct request *rq) { struct bio_vec bvec; struct req_iterator iter; - struct loop_device *lo; int ret = 0; + struct xloop_device *xlo; loff_t pos; - lo = loop_file_fmt_get_lo(lo_fmt); + xlo = xloop_file_fmt_get_xlo(xlo_fmt); + pos = __raw_file_fmt_rq_get_pos(xlo_fmt, rq); - if (lo->transfer) - return raw_file_fmt_write_transfer(lo_fmt, rq); - - pos = __raw_file_fmt_rq_get_pos(lo_fmt, rq); + if (xlo->transfer) + return __raw_file_fmt_write_transfer(xlo, rq, pos); rq_for_each_segment(bvec, rq, iter) { - ret = __raw_file_fmt_write_bvec(lo->lo_backing_file, &bvec, - &pos); + ret = __raw_file_fmt_write_bvec(xlo->xlo_backing_file, &bvec, &pos); if (ret < 0) break; cond_resched(); @@ -339,29 +327,32 @@ static int raw_file_fmt_write(struct loop_file_fmt *lo_fmt, return ret; } -static int raw_file_fmt_write_aio(struct loop_file_fmt *lo_fmt, +static int raw_file_fmt_write_aio(struct xloop_file_fmt *xlo_fmt, struct request *rq) { - return __raw_file_fmt_rw_aio(lo_fmt, rq, WRITE); + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); + struct xloop_cmd *cmd = blk_mq_rq_to_pdu(rq); + loff_t pos = __raw_file_fmt_rq_get_pos(xlo_fmt, rq); + + return __raw_file_fmt_rw_aio(xlo, cmd, pos, WRITE); } -static int raw_file_fmt_discard(struct loop_file_fmt *lo_fmt, - struct request *rq) +static int __raw_file_fmt_fallocate(struct xloop_device *xlo, + struct request *rq, loff_t pos, int mode) { - loff_t pos = __raw_file_fmt_rq_get_pos(lo_fmt, rq); - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); - /* - * We use punch hole to reclaim the free space used by the - * image a.k.a. discard. However we do not support discard if - * encryption is enabled, because it may give an attacker - * useful information. + * We use fallocate to manipulate the space mappings used by the image + * a.k.a. discard/zerorange. However we do not support this if + * encryption is enabled, because it may give an attacker useful + * information. */ - struct file *file = lo->lo_backing_file; - int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE; + struct file *file = xlo->xlo_backing_file; + struct request_queue *q = xlo->xlo_queue; int ret; - if ((!file->f_op->fallocate) || lo->lo_encrypt_key_size) { + mode |= FALLOC_FL_KEEP_SIZE; + + if (!blk_queue_discard(q)) { ret = -EOPNOTSUPP; goto out; } @@ -369,14 +360,39 @@ static int raw_file_fmt_discard(struct loop_file_fmt *lo_fmt, ret = file->f_op->fallocate(file, mode, pos, blk_rq_bytes(rq)); if (unlikely(ret && ret != -EINVAL && ret != -EOPNOTSUPP)) ret = -EIO; - out: +out: return ret; } -static int raw_file_fmt_flush(struct loop_file_fmt *lo_fmt) +static int raw_file_fmt_write_zeros(struct xloop_file_fmt *xlo_fmt, + struct request *rq) { - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); - struct file *file = lo->lo_backing_file; + loff_t pos = __raw_file_fmt_rq_get_pos(xlo_fmt, rq); + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); + + /* + * If the caller doesn't want deallocation, call zeroout to + * write zeroes the range. Otherwise, punch them out. + */ + return __raw_file_fmt_fallocate(xlo, rq, pos, + (rq->cmd_flags & REQ_NOUNMAP) ? + FALLOC_FL_ZERO_RANGE : + FALLOC_FL_PUNCH_HOLE); +} + +static int raw_file_fmt_discard(struct xloop_file_fmt *xlo_fmt, + struct request *rq) +{ + loff_t pos = __raw_file_fmt_rq_get_pos(xlo_fmt, rq); + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); + + return __raw_file_fmt_fallocate(xlo, rq, pos, FALLOC_FL_PUNCH_HOLE); +} + +static int raw_file_fmt_flush(struct xloop_file_fmt *xlo_fmt) +{ + struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt); + struct file *file = xlo->xlo_backing_file; int ret = vfs_fsync(file, 0); if (unlikely(ret && ret != -EINVAL)) ret = -EIO; @@ -384,66 +400,66 @@ static int raw_file_fmt_flush(struct loop_file_fmt *lo_fmt) return ret; } -static loff_t raw_file_fmt_sector_size(struct loop_file_fmt *lo_fmt) +static loff_t raw_file_fmt_sector_size(struct xloop_file_fmt *xlo_fmt, + struct file *file, loff_t offset, loff_t sizelimit) { - struct loop_device *lo = loop_file_fmt_get_lo(lo_fmt); - loff_t loopsize; + loff_t xloopsize; - /* Compute loopsize in bytes */ - loopsize = i_size_read(lo->lo_backing_file->f_mapping->host); - if (lo->lo_offset > 0) - loopsize -= lo->lo_offset; + /* Compute xloopsize in bytes */ + xloopsize = i_size_read(file->f_mapping->host); + if (offset > 0) + xloopsize -= offset; /* offset is beyond i_size, weird but possible */ - if (loopsize < 0) + if (xloopsize < 0) return 0; - if (lo->lo_sizelimit > 0 && lo->lo_sizelimit < loopsize) - loopsize = lo->lo_sizelimit; - + if (sizelimit > 0 && sizelimit < xloopsize) + xloopsize = sizelimit; /* * Unfortunately, if we want to do I/O on the device, * the number of 512-byte sectors has to fit into a sector_t. */ - return loopsize >> 9; + return xloopsize >> 9; } -static struct loop_file_fmt_ops raw_file_fmt_ops = { +static struct xloop_file_fmt_ops raw_file_fmt_ops = { .init = NULL, .exit = NULL, .read = raw_file_fmt_read, .write = raw_file_fmt_write, .read_aio = raw_file_fmt_read_aio, .write_aio = raw_file_fmt_write_aio, + .write_zeros = raw_file_fmt_write_zeros, .discard = raw_file_fmt_discard, .flush = raw_file_fmt_flush, - .sector_size = raw_file_fmt_sector_size + .sector_size = raw_file_fmt_sector_size, }; -static struct loop_file_fmt_driver raw_file_fmt_driver = { +static struct xloop_file_fmt_driver raw_file_fmt_driver = { .name = "RAW", - .file_fmt_type = LO_FILE_FMT_RAW, + .file_fmt_type = XLO_FILE_FMT_RAW, .ops = &raw_file_fmt_ops, - .owner = THIS_MODULE + .owner = THIS_MODULE, }; -static int __init loop_file_fmt_raw_init(void) +static int __init xloop_file_fmt_raw_init(void) { - printk(KERN_INFO "loop_file_fmt_raw: init loop device RAW file format " + printk(KERN_INFO "xloop_file_fmt_raw: init xloop device RAW file format " "driver"); - return loop_file_fmt_register_driver(&raw_file_fmt_driver); + return xloop_file_fmt_register_driver(&raw_file_fmt_driver); } -static void __exit loop_file_fmt_raw_exit(void) +static void __exit xloop_file_fmt_raw_exit(void) { - printk(KERN_INFO "loop_file_fmt_raw: exit loop device RAW file format " + printk(KERN_INFO "xloop_file_fmt_raw: exit xloop device RAW file format " "driver"); - loop_file_fmt_unregister_driver(&raw_file_fmt_driver); + xloop_file_fmt_unregister_driver(&raw_file_fmt_driver); } -module_init(loop_file_fmt_raw_init); -module_exit(loop_file_fmt_raw_exit); +module_init(xloop_file_fmt_raw_init); +module_exit(xloop_file_fmt_raw_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Manuel Bentele <development@manuel-bentele.de>"); -MODULE_DESCRIPTION("Loop device RAW file format driver"); -MODULE_SOFTDEP("pre: loop"); +MODULE_DESCRIPTION("xloop device RAW file format driver"); +MODULE_SOFTDEP("pre: xloop"); diff --git a/loop_main.c b/loop_main.c index 9cf56c2..9cd9c99 100644 --- a/loop_main.c +++ b/loop_main.c @@ -1,7 +1,7 @@ /* - * loop_main.c + * loop_main.c * - * Written by Theodore Ts'o, 3/29/93 + * Written by Theodore Ts'o, 3/29/93 * * Copyright 1993 by Theodore Ts'o. Redistribution of this file is * permitted under the GNU General Public License. @@ -12,7 +12,7 @@ * Modularized and updated for 1.1.16 kernel - Mitch Dsouza 28th May 1994 * Adapted for 1.3.59 kernel - Andries Brouwer, 1 Feb 1996 * - * Fixed do_loop_request() re-entrancy - Vincent.Renardias@waw.com Mar 20, 1997 + * Fixed do_xloop_request() re-entrancy - Vincent.Renardias@waw.com Mar 20, 1997 * * Added devfs support - Richard Gooch <rgooch@atnf.csiro.au> 16-Jan-1998 * @@ -24,11 +24,11 @@ * CBC (and relatives) mode encryption requiring unique IVs per data block. * Reed H. Petty, rhp@draper.net * - * Maximum number of loop devices now dynamic via max_loop module parameter. + * Maximum number of xloop devices now dynamic via max_xloop module parameter. * Russell Kroll <rkroll@exploits.org> 19990701 * - * Maximum number of loop devices when compiled-in now selectable by passing - * max_loop=<1-255> to the kernel on boot. + * Maximum number of xloop devices when compiled-in now selectable by passing + * max_xloop=<1-255> to the kernel on boot. * Erik I. Bolsø, <eriki@himolde.no>, Oct 31, 1999 * * Completely rewrite request handling to be make_request_fn style and @@ -36,7 +36,7 @@ * Al Viro too. * Jens Axboe <axboe@suse.de>, Nov 2000 * - * Support up to 256 loop devices + * Support up to 256 xloop devices * Heinz Mauelshagen <mge@sistina.com>, Feb 2002 * * Support for falling back on the write file operation when the address space @@ -59,6 +59,7 @@ #include <linux/file.h> #include <linux/stat.h> #include <linux/errno.h> +#include <linux/major.h> #include <linux/wait.h> #include <linux/blkdev.h> #include <linux/blkpg.h> @@ -80,99 +81,83 @@ #include <linux/uio.h> #include <linux/ioprio.h> #include <linux/blk-cgroup.h> +#include <linux/version.h> +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> +#endif #include "loop_file_fmt.h" #include "loop_main.h" #include <linux/uaccess.h> -static DEFINE_IDR(loop_index_idr); -static DEFINE_MUTEX(loop_ctl_mutex); +static DEFINE_IDR(xloop_index_idr); +static DEFINE_MUTEX(xloop_ctl_mutex); static int max_part; static int part_shift; -static int transfer_xor(struct loop_device *lo, int cmd, +static int transfer_xor(struct xloop_device *xlo, int cmd, struct page *raw_page, unsigned raw_off, - struct page *loop_page, unsigned loop_off, + struct page *xloop_page, unsigned xloop_off, int size, sector_t real_block) { char *raw_buf = kmap_atomic(raw_page) + raw_off; - char *loop_buf = kmap_atomic(loop_page) + loop_off; + char *xloop_buf = kmap_atomic(xloop_page) + xloop_off; char *in, *out, *key; int i, keysize; if (cmd == READ) { in = raw_buf; - out = loop_buf; + out = xloop_buf; } else { - in = loop_buf; + in = xloop_buf; out = raw_buf; } - key = lo->lo_encrypt_key; - keysize = lo->lo_encrypt_key_size; + key = xlo->xlo_encrypt_key; + keysize = xlo->xlo_encrypt_key_size; for (i = 0; i < size; i++) *out++ = *in++ ^ key[(i & 511) % keysize]; - kunmap_atomic(loop_buf); + kunmap_atomic(xloop_buf); kunmap_atomic(raw_buf); cond_resched(); return 0; } -static int xor_init(struct loop_device *lo, const struct xloop_info64 *info) +static int xor_init(struct xloop_device *xlo, const struct xloop_info64 *info) { - if (unlikely(info->lo_encrypt_key_size <= 0)) + if (unlikely(info->xlo_encrypt_key_size <= 0)) return -EINVAL; return 0; } -static struct loop_func_table none_funcs = { - .number = LO_CRYPT_NONE, +static struct xloop_func_table none_funcs = { + .number = XLO_CRYPT_NONE, }; -static struct loop_func_table xor_funcs = { - .number = LO_CRYPT_XOR, +static struct xloop_func_table xor_funcs = { + .number = XLO_CRYPT_XOR, .transfer = transfer_xor, .init = xor_init }; /* xfer_funcs[0] is special - its release function is never called */ -static struct loop_func_table *xfer_funcs[MAX_LO_CRYPT] = { +static struct xloop_func_table *xfer_funcs[MAX_XLO_CRYPT] = { &none_funcs, &xor_funcs }; -static loff_t get_size(loff_t offset, loff_t sizelimit, struct file *file) -{ - loff_t loopsize; - - /* Compute loopsize in bytes */ - loopsize = i_size_read(file->f_mapping->host); - if (offset > 0) - loopsize -= offset; - /* offset is beyond i_size, weird but possible */ - if (loopsize < 0) - return 0; - - if (sizelimit > 0 && sizelimit < loopsize) - loopsize = sizelimit; - /* - * Unfortunately, if we want to do I/O on the device, - * the number of 512-byte sectors has to fit into a sector_t. - */ - return loopsize >> 9; -} - -static loff_t get_loop_size(struct loop_device *lo, struct file *file) +static loff_t get_xloop_size(struct xloop_device *xlo, struct file *file) { - return get_size(lo->lo_offset, lo->lo_sizelimit, file); + return xloop_file_fmt_sector_size(xlo->xlo_fmt, file, xlo->xlo_offset, + xlo->xlo_sizelimit); } -static void __loop_update_dio(struct loop_device *lo, bool dio) +static void __xloop_update_dio(struct xloop_device *xlo, bool dio) { - struct file *file = lo->lo_backing_file; + struct file *file = xlo->xlo_backing_file; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; unsigned short sb_bsize = 0; @@ -185,9 +170,9 @@ static void __loop_update_dio(struct loop_device *lo, bool dio) } /* - * We support direct I/O only if lo_offset is aligned with the + * We support direct I/O only if xlo_offset is aligned with the * logical I/O size of backing device, and the logical block - * size of loop is bigger than the backing device's and the loop + * size of xloop is bigger than the backing device's and the xloop * needn't transform transfer. * * TODO: the above condition may be loosed in the future, and @@ -195,10 +180,10 @@ static void __loop_update_dio(struct loop_device *lo, bool dio) * of requests in sane applications should be PAGE_SIZE aligned */ if (dio) { - if (queue_logical_block_size(lo->lo_queue) >= sb_bsize && - !(lo->lo_offset & dio_align) && + if (queue_logical_block_size(xlo->xlo_queue) >= sb_bsize && + !(xlo->xlo_offset & dio_align) && mapping->a_ops->direct_IO && - !lo->transfer) + !xlo->transfer) use_dio = true; else use_dio = false; @@ -206,58 +191,83 @@ static void __loop_update_dio(struct loop_device *lo, bool dio) use_dio = false; } - if (lo->use_dio == use_dio) + if (xlo->use_dio == use_dio) return; /* flush dirty pages before changing direct IO */ - loop_file_fmt_flush(lo->lo_fmt); + xloop_file_fmt_flush(xlo->xlo_fmt); /* - * The flag of LO_FLAGS_DIRECT_IO is handled similarly with - * LO_FLAGS_READ_ONLY, both are set from kernel, and losetup - * will get updated by ioctl(LOOP_GET_STATUS) + * The flag of XLO_FLAGS_DIRECT_IO is handled similarly with + * XLO_FLAGS_READ_ONLY, both are set from kernel, and losetup + * will get updated by ioctl(XLOOP_GET_STATUS) */ - blk_mq_freeze_queue(lo->lo_queue); - lo->use_dio = use_dio; + if (xlo->xlo_state == Xlo_bound) + blk_mq_freeze_queue(xlo->xlo_queue); + xlo->use_dio = use_dio; if (use_dio) { - blk_queue_flag_clear(QUEUE_FLAG_NOMERGES, lo->lo_queue); - lo->lo_flags |= LO_FLAGS_DIRECT_IO; + blk_queue_flag_clear(QUEUE_FLAG_NOMERGES, xlo->xlo_queue); + xlo->xlo_flags |= XLO_FLAGS_DIRECT_IO; } else { - blk_queue_flag_set(QUEUE_FLAG_NOMERGES, lo->lo_queue); - lo->lo_flags &= ~LO_FLAGS_DIRECT_IO; + blk_queue_flag_set(QUEUE_FLAG_NOMERGES, xlo->xlo_queue); + xlo->xlo_flags &= ~XLO_FLAGS_DIRECT_IO; } - blk_mq_unfreeze_queue(lo->lo_queue); + if (xlo->xlo_state == Xlo_bound) + blk_mq_unfreeze_queue(xlo->xlo_queue); } +/** + * xloop_validate_block_size() - validates the passed in block size + * @bsize: size to validate + */ static int -figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit) -{ - loff_t size = loop_file_fmt_sector_size(lo->lo_fmt); - sector_t x = (sector_t)size; - struct block_device *bdev = lo->lo_device; - - if (unlikely((loff_t)x != size)) - return -EFBIG; - if (lo->lo_offset != offset) - lo->lo_offset = offset; - if (lo->lo_sizelimit != sizelimit) - lo->lo_sizelimit = sizelimit; - set_capacity(lo->lo_disk, x); - bd_set_size(bdev, (loff_t)get_capacity(bdev->bd_disk) << 9); - /* let user-space know about the new size */ - kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE); +xloop_validate_block_size(unsigned short bsize) +{ + if (bsize < 512 || bsize > PAGE_SIZE || !is_power_of_2(bsize)) + return -EINVAL; + return 0; } -static void lo_complete_rq(struct request *rq) +/** + * xloop_set_size() - sets device size and notifies userspace + * @xlo: struct xloop_device to set the size for + * @size: new size of the xloop device + * + * Callers must validate that the size passed into this function fits into + * a sector_t, eg using xloop_validate_size() + */ +static void xloop_set_size(struct xloop_device *xlo, loff_t size) { - struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq); + struct block_device *bdev = xlo->xlo_device; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0) + sector_t capacity; +#endif + + bd_set_size(bdev, size << SECTOR_SHIFT); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) + set_capacity_revalidate_and_notify(xlo->xlo_disk, size, false); +#else + capacity = get_capacity(xlo->xlo_disk); + set_capacity(xlo->xlo_disk, size); + if (capacity != size && capacity != 0 && size != 0) { + char *envp[] = { "RESIZE=1", NULL }; + kobject_uevent_env(&disk_to_dev(xlo->xlo_disk)->kobj, KOBJ_CHANGE, + envp); + } +#endif +} + +static void xlo_complete_rq(struct request *rq) +{ + struct xloop_cmd *cmd = blk_mq_rq_to_pdu(rq); blk_status_t ret = BLK_STS_OK; if (!cmd->use_aio || cmd->ret < 0 || cmd->ret == blk_rq_bytes(rq) || req_op(rq) != REQ_OP_READ) { if (cmd->ret < 0) - ret = BLK_STS_IOERR; + ret = errno_to_blk_status(cmd->ret); goto end_io; } @@ -284,73 +294,89 @@ end_io: } } -static int do_req_filebacked(struct loop_device *lo, struct request *rq) +static int do_req_filebacked(struct xloop_device *xlo, struct request *rq) { - struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq); + struct xloop_cmd *cmd = blk_mq_rq_to_pdu(rq); + /* + * xlo_write_simple and xlo_read_simple should have been covered + * by io submit style function like xlo_rw_aio(), one blocker + * is that xlo_read_simple() need to call flush_dcache_page after + * the page is written from kernel, and it isn't easy to handle + * this in io submit style function which submits all segments + * of the req at one time. And direct read IO doesn't need to + * run flush_dcache_page(). + */ switch (req_op(rq)) { case REQ_OP_FLUSH: - return loop_file_fmt_flush(lo->lo_fmt); - case REQ_OP_DISCARD: + return xloop_file_fmt_flush(xlo->xlo_fmt); case REQ_OP_WRITE_ZEROES: - return loop_file_fmt_discard(lo->lo_fmt, rq); + return xloop_file_fmt_write_zeros(xlo->xlo_fmt, rq); + case REQ_OP_DISCARD: + return xloop_file_fmt_discard(xlo->xlo_fmt, rq); case REQ_OP_WRITE: if (cmd->use_aio) - return loop_file_fmt_write_aio(lo->lo_fmt, rq); + return xloop_file_fmt_write_aio(xlo->xlo_fmt, rq); else - return loop_file_fmt_write(lo->lo_fmt, rq); + return xloop_file_fmt_write(xlo->xlo_fmt, rq); case REQ_OP_READ: if (cmd->use_aio) - return loop_file_fmt_read_aio(lo->lo_fmt, rq); + return xloop_file_fmt_read_aio(xlo->xlo_fmt, rq); else - return loop_file_fmt_read(lo->lo_fmt, rq); + return xloop_file_fmt_read(xlo->xlo_fmt, rq); default: WARN_ON_ONCE(1); return -EIO; } } -static inline void loop_update_dio(struct loop_device *lo) +static inline void xloop_update_dio(struct xloop_device *xlo) { - __loop_update_dio(lo, io_is_direct(lo->lo_backing_file) | - lo->use_dio); + __xloop_update_dio(xlo, (xlo->xlo_backing_file->f_flags & O_DIRECT) | + xlo->use_dio); } -static void loop_reread_partitions(struct loop_device *lo, +static void xloop_reread_partitions(struct xloop_device *xlo, struct block_device *bdev) { int rc; + mutex_lock(&bdev->bd_mutex); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0) + rc = bdev_disk_changed(bdev, false); +#else rc = blkdev_reread_part(bdev); +#endif + mutex_unlock(&bdev->bd_mutex); if (rc) pr_warn("%s: partition scan of xloop%d (%s) failed (rc=%d)\n", - __func__, lo->lo_number, lo->lo_file_name, rc); + __func__, xlo->xlo_number, xlo->xlo_file_name, rc); } -static inline int is_loop_device(struct file *file) +static inline int is_xloop_device(struct file *file) { struct inode *i = file->f_mapping->host; - return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == XLOOP_MAJOR; + return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR; } -static int loop_validate_file(struct file *file, struct block_device *bdev) +static int xloop_validate_file(struct file *file, struct block_device *bdev) { struct inode *inode = file->f_mapping->host; struct file *f = file; /* Avoid recursion */ - while (is_loop_device(f)) { - struct loop_device *l; + while (is_xloop_device(f)) { + struct xloop_device *l; if (f->f_mapping->host->i_bdev == bdev) return -EBADF; l = f->f_mapping->host->i_bdev->bd_disk->private_data; - if (l->lo_state != Lo_bound) { + if (l->xlo_state != Xlo_bound) { return -EINVAL; } - f = l->lo_backing_file; + f = l->xlo_backing_file; } if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) return -EINVAL; @@ -358,30 +384,30 @@ static int loop_validate_file(struct file *file, struct block_device *bdev) } /* - * loop_change_fd switched the backing store of a loopback device to + * xloop_change_fd switched the backing store of a xloopback device to * a new file. This is useful for operating system installers to free up * the original file and in High Availability environments to switch to * an alternative location for the content in case of server meltdown. - * This can only work if the loop device is used read-only, and if the + * This can only work if the xloop device is used read-only, and if the * new backing store is the same size and type as the old backing store. */ -static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, +static int xloop_change_fd(struct xloop_device *xlo, struct block_device *bdev, unsigned int arg) { struct file *file = NULL, *old_file; int error; bool partscan; - error = mutex_lock_killable(&loop_ctl_mutex); + error = mutex_lock_killable(&xloop_ctl_mutex); if (error) return error; error = -ENXIO; - if (lo->lo_state != Lo_bound) + if (xlo->xlo_state != Xlo_bound) goto out_err; - /* the loop device has to be read-only */ + /* the xloop device has to be read-only */ error = -EINVAL; - if (!(lo->lo_flags & LO_FLAGS_READ_ONLY)) + if (!(xlo->xlo_flags & XLO_FLAGS_READ_ONLY)) goto out_err; error = -EBADF; @@ -389,76 +415,76 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, if (!file) goto out_err; - error = loop_validate_file(file, bdev); + error = xloop_validate_file(file, bdev); if (error) goto out_err; - old_file = lo->lo_backing_file; + old_file = xlo->xlo_backing_file; error = -EINVAL; /* size of the new backing store needs to be the same */ - if (get_loop_size(lo, file) != get_loop_size(lo, old_file)) + if (get_xloop_size(xlo, file) != get_xloop_size(xlo, old_file)) goto out_err; /* and ... switch */ - blk_mq_freeze_queue(lo->lo_queue); - mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask); - lo->lo_backing_file = file; - lo->old_gfp_mask = mapping_gfp_mask(file->f_mapping); + blk_mq_freeze_queue(xlo->xlo_queue); + mapping_set_gfp_mask(old_file->f_mapping, xlo->old_gfp_mask); + xlo->xlo_backing_file = file; + xlo->old_gfp_mask = mapping_gfp_mask(file->f_mapping); mapping_set_gfp_mask(file->f_mapping, - lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); - loop_update_dio(lo); - blk_mq_unfreeze_queue(lo->lo_queue); - partscan = lo->lo_flags & LO_FLAGS_PARTSCAN; - mutex_unlock(&loop_ctl_mutex); + xlo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); + xloop_update_dio(xlo); + blk_mq_unfreeze_queue(xlo->xlo_queue); + partscan = xlo->xlo_flags & XLO_FLAGS_PARTSCAN; + mutex_unlock(&xloop_ctl_mutex); /* - * We must drop file reference outside of loop_ctl_mutex as dropping + * We must drop file reference outside of xloop_ctl_mutex as dropping * the file ref can take bd_mutex which creates circular locking * dependency. */ fput(old_file); if (partscan) - loop_reread_partitions(lo, bdev); + xloop_reread_partitions(xlo, bdev); return 0; out_err: - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&xloop_ctl_mutex); if (file) fput(file); return error; } -/* loop sysfs attributes */ +/* xloop sysfs attributes */ -static ssize_t loop_attr_show(struct device *dev, char *page, - ssize_t (*callback)(struct loop_device *, char *)) +static ssize_t xloop_attr_show(struct device *dev, char *page, + ssize_t (*callback)(struct xloop_device *, char *)) { struct gendisk *disk = dev_to_disk(dev); - struct loop_device *lo = disk->private_data; + struct xloop_device *xlo = disk->private_data; - return callback(lo, page); + return callback(xlo, page); } -#define LOOP_ATTR_RO(_name) \ -static ssize_t loop_attr_##_name##_show(struct loop_device *, char *); \ -static ssize_t loop_attr_do_show_##_name(struct device *d, \ +#define XLOOP_ATTR_RO(_name) \ +static ssize_t xloop_attr_##_name##_show(struct xloop_device *, char *); \ +static ssize_t xloop_attr_do_show_##_name(struct device *d, \ struct device_attribute *attr, char *b) \ { \ - return loop_attr_show(d, b, loop_attr_##_name##_show); \ + return xloop_attr_show(d, b, xloop_attr_##_name##_show); \ } \ -static struct device_attribute loop_attr_##_name = \ - __ATTR(_name, 0444, loop_attr_do_show_##_name, NULL); +static struct device_attribute xloop_attr_##_name = \ + __ATTR(_name, 0444, xloop_attr_do_show_##_name, NULL); -static ssize_t loop_attr_backing_file_show(struct loop_device *lo, char *buf) +static ssize_t xloop_attr_backing_file_show(struct xloop_device *xlo, char *buf) { ssize_t ret; char *p = NULL; - spin_lock_irq(&lo->lo_lock); - if (lo->lo_backing_file) - p = file_path(lo->lo_backing_file, buf, PAGE_SIZE - 1); - spin_unlock_irq(&lo->lo_lock); + spin_lock_irq(&xlo->xlo_lock); + if (xlo->xlo_backing_file) + p = file_path(xlo->xlo_backing_file, buf, PAGE_SIZE - 1); + spin_unlock_irq(&xlo->xlo_lock); if (IS_ERR_OR_NULL(p)) ret = PTR_ERR(p); @@ -472,89 +498,107 @@ static ssize_t loop_attr_backing_file_show(struct loop_device *lo, char *buf) return ret; } -static ssize_t loop_attr_file_fmt_type_show(struct loop_device *lo, char *buf) +static ssize_t xloop_attr_file_fmt_type_show(struct xloop_device *xlo, + char *buf) { ssize_t len = 0; - len = loop_file_fmt_print_type(lo->lo_fmt->file_fmt_type, buf); + len = xloop_file_fmt_print_type(xlo->xlo_fmt->file_fmt_type, buf); len += sprintf(buf + len, "\n"); return len; } -static ssize_t loop_attr_offset_show(struct loop_device *lo, char *buf) +static ssize_t xloop_attr_offset_show(struct xloop_device *xlo, char *buf) { - return sprintf(buf, "%llu\n", (unsigned long long)lo->lo_offset); + return sprintf(buf, "%llu\n", (unsigned long long)xlo->xlo_offset); } -static ssize_t loop_attr_sizelimit_show(struct loop_device *lo, char *buf) +static ssize_t xloop_attr_sizelimit_show(struct xloop_device *xlo, char *buf) { - return sprintf(buf, "%llu\n", (unsigned long long)lo->lo_sizelimit); + return sprintf(buf, "%llu\n", (unsigned long long)xlo->xlo_sizelimit); } -static ssize_t loop_attr_autoclear_show(struct loop_device *lo, char *buf) +static ssize_t xloop_attr_autoclear_show(struct xloop_device *xlo, char *buf) { - int autoclear = (lo->lo_flags & LO_FLAGS_AUTOCLEAR); + int autoclear = (xlo->xlo_flags & XLO_FLAGS_AUTOCLEAR); return sprintf(buf, "%s\n", autoclear ? "1" : "0"); } -static ssize_t loop_attr_partscan_show(struct loop_device *lo, char *buf) +static ssize_t xloop_attr_partscan_show(struct xloop_device *xlo, char *buf) { - int partscan = (lo->lo_flags & LO_FLAGS_PARTSCAN); + int partscan = (xlo->xlo_flags & XLO_FLAGS_PARTSCAN); return sprintf(buf, "%s\n", partscan ? "1" : "0"); } -static ssize_t loop_attr_dio_show(struct loop_device *lo, char *buf) +static ssize_t xloop_attr_dio_show(struct xloop_device *xlo, char *buf) { - int dio = (lo->lo_flags & LO_FLAGS_DIRECT_IO); + int dio = (xlo->xlo_flags & XLO_FLAGS_DIRECT_IO); return sprintf(buf, "%s\n", dio ? "1" : "0"); } -LOOP_ATTR_RO(backing_file); -LOOP_ATTR_RO(file_fmt_type); -LOOP_ATTR_RO(offset); -LOOP_ATTR_RO(sizelimit); -LOOP_ATTR_RO(autoclear); -LOOP_ATTR_RO(partscan); -LOOP_ATTR_RO(dio); - -static struct attribute *loop_attrs[] = { - &loop_attr_backing_file.attr, - &loop_attr_file_fmt_type.attr, - &loop_attr_offset.attr, - &loop_attr_sizelimit.attr, - &loop_attr_autoclear.attr, - &loop_attr_partscan.attr, - &loop_attr_dio.attr, +XLOOP_ATTR_RO(backing_file); +XLOOP_ATTR_RO(file_fmt_type); +XLOOP_ATTR_RO(offset); +XLOOP_ATTR_RO(sizelimit); +XLOOP_ATTR_RO(autoclear); +XLOOP_ATTR_RO(partscan); +XLOOP_ATTR_RO(dio); + +static struct attribute *xloop_attrs[] = { + &xloop_attr_backing_file.attr, + &xloop_attr_file_fmt_type.attr, + &xloop_attr_offset.attr, + &xloop_attr_sizelimit.attr, + &xloop_attr_autoclear.attr, + &xloop_attr_partscan.attr, + &xloop_attr_dio.attr, NULL, }; -static struct attribute_group loop_attribute_group = { +static struct attribute_group xloop_attribute_group = { .name = "xloop", - .attrs= loop_attrs, + .attrs= xloop_attrs, }; -static void loop_sysfs_init(struct loop_device *lo) +static void xloop_sysfs_init(struct xloop_device *xlo) { - lo->sysfs_inited = !sysfs_create_group(&disk_to_dev(lo->lo_disk)->kobj, - &loop_attribute_group); + xlo->sysfs_inited = !sysfs_create_group(&disk_to_dev(xlo->xlo_disk)->kobj, + &xloop_attribute_group); } -static void loop_sysfs_exit(struct loop_device *lo) +static void xloop_sysfs_exit(struct xloop_device *xlo) { - if (lo->sysfs_inited) - sysfs_remove_group(&disk_to_dev(lo->lo_disk)->kobj, - &loop_attribute_group); + if (xlo->sysfs_inited) + sysfs_remove_group(&disk_to_dev(xlo->xlo_disk)->kobj, + &xloop_attribute_group); } -static void loop_config_discard(struct loop_device *lo) +static void xloop_config_discard(struct xloop_device *xlo) { - struct file *file = lo->lo_backing_file; + struct file *file = xlo->xlo_backing_file; struct inode *inode = file->f_mapping->host; - struct request_queue *q = lo->lo_queue; + struct request_queue *q = xlo->xlo_queue; + u32 granularity, max_discard_sectors; + + /* + * If the backing device is a block device, mirror its zeroing + * capability. Set the discard sectors to the block device's zeroing + * capabilities because xloop discards result in blkdev_issue_zeroout(), + * not blkdev_issue_discard(). This maintains consistent behavior with + * file-backed xloop devices: discarded regions read back as zero. + */ + if (S_ISBLK(inode->i_mode) && !xlo->xlo_encrypt_key_size) { + struct request_queue *backingq; + + backingq = bdev_get_queue(inode->i_bdev); + + max_discard_sectors = backingq->limits.max_write_zeroes_sectors; + granularity = backingq->limits.discard_granularity ?: + queue_physical_block_size(backingq); /* * We use punch hole to reclaim the free space used by the @@ -562,53 +606,62 @@ static void loop_config_discard(struct loop_device *lo) * encryption is enabled, because it may give an attacker * useful information. */ - if ((!file->f_op->fallocate) || - lo->lo_encrypt_key_size) { + } else if (!file->f_op->fallocate || xlo->xlo_encrypt_key_size) { + max_discard_sectors = 0; + granularity = 0; + + } else { + max_discard_sectors = UINT_MAX >> 9; + granularity = inode->i_sb->s_blocksize; + } + + if (max_discard_sectors) { + q->limits.discard_granularity = granularity; + blk_queue_max_discard_sectors(q, max_discard_sectors); + blk_queue_max_write_zeroes_sectors(q, max_discard_sectors); + blk_queue_flag_set(QUEUE_FLAG_DISCARD, q); + } else { q->limits.discard_granularity = 0; - q->limits.discard_alignment = 0; blk_queue_max_discard_sectors(q, 0); blk_queue_max_write_zeroes_sectors(q, 0); blk_queue_flag_clear(QUEUE_FLAG_DISCARD, q); - return; } - - q->limits.discard_granularity = inode->i_sb->s_blocksize; q->limits.discard_alignment = 0; - - blk_queue_max_discard_sectors(q, UINT_MAX >> 9); - blk_queue_max_write_zeroes_sectors(q, UINT_MAX >> 9); - blk_queue_flag_set(QUEUE_FLAG_DISCARD, q); } -static void loop_unprepare_queue(struct loop_device *lo) +static void xloop_unprepare_queue(struct xloop_device *xlo) { - kthread_flush_worker(&lo->worker); - kthread_stop(lo->worker_task); + kthread_flush_worker(&xlo->worker); + kthread_stop(xlo->worker_task); } -static int loop_kthread_worker_fn(void *worker_ptr) +static int xloop_kthread_worker_fn(void *worker_ptr) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO; +#else current->flags |= PF_LESS_THROTTLE | PF_MEMALLOC_NOIO; +#endif return kthread_worker_fn(worker_ptr); } -static int loop_prepare_queue(struct loop_device *lo) +static int xloop_prepare_queue(struct xloop_device *xlo) { - kthread_init_worker(&lo->worker); - lo->worker_task = kthread_run(loop_kthread_worker_fn, - &lo->worker, "xloop%d", lo->lo_number); - if (IS_ERR(lo->worker_task)) + kthread_init_worker(&xlo->worker); + xlo->worker_task = kthread_run(xloop_kthread_worker_fn, + &xlo->worker, "xloop%d", xlo->xlo_number); + if (IS_ERR(xlo->worker_task)) return -ENOMEM; - set_user_nice(lo->worker_task, MIN_NICE); + set_user_nice(xlo->worker_task, MIN_NICE); return 0; } -static void loop_update_rotational(struct loop_device *lo) +static void xloop_update_rotational(struct xloop_device *xlo) { - struct file *file = lo->lo_backing_file; + struct file *file = xlo->xlo_backing_file; struct inode *file_inode = file->f_mapping->host; struct block_device *file_bdev = file_inode->i_sb->s_bdev; - struct request_queue *q = lo->lo_queue; + struct request_queue *q = xlo->xlo_queue; bool nonrot = true; /* not all filesystems (e.g. tmpfs) have a sb->s_bdev */ @@ -621,23 +674,125 @@ static void loop_update_rotational(struct loop_device *lo) blk_queue_flag_clear(QUEUE_FLAG_NONROT, q); } -static int loop_set_fd(struct loop_device *lo, fmode_t mode, - struct block_device *bdev, unsigned int arg) +static int +xloop_release_xfer(struct xloop_device *xlo) +{ + int err = 0; + struct xloop_func_table *xfer = xlo->xlo_encryption; + + if (xfer) { + if (xfer->release) + err = xfer->release(xlo); + xlo->transfer = NULL; + xlo->xlo_encryption = NULL; + module_put(xfer->owner); + } + return err; +} + +static int +xloop_init_xfer(struct xloop_device *xlo, struct xloop_func_table *xfer, + const struct xloop_info64 *i) +{ + int err = 0; + + if (xfer) { + struct module *owner = xfer->owner; + + if (!try_module_get(owner)) + return -EINVAL; + if (xfer->init) + err = xfer->init(xlo, i); + if (err) + module_put(owner); + else + xlo->xlo_encryption = xfer; + } + return err; +} + +/** + * xloop_set_status_from_info - configure device from xloop_info + * @xlo: struct xloop_device to configure + * @info: struct xloop_info64 to configure the device with + * + * Configures the xloop device parameters according to the passed + * in xloop_info64 configuration. + */ +static int +xloop_set_status_from_info(struct xloop_device *xlo, + const struct xloop_info64 *info) +{ + int err; + struct xloop_func_table *xfer; + kuid_t uid = current_uid(); + + if ((unsigned int) info->xlo_encrypt_key_size > XLO_KEY_SIZE) + return -EINVAL; + + err = xloop_release_xfer(xlo); + if (err) + return err; + + if (info->xlo_encrypt_type) { + unsigned int type = info->xlo_encrypt_type; + + if (type >= MAX_XLO_CRYPT) + return -EINVAL; + xfer = xfer_funcs[type]; + if (xfer == NULL) + return -EINVAL; + } else + xfer = NULL; + + err = xloop_init_xfer(xlo, xfer, info); + if (err) + return err; + + xlo->xlo_offset = info->xlo_offset; + xlo->xlo_sizelimit = info->xlo_sizelimit; + memcpy(xlo->xlo_file_name, info->xlo_file_name, XLO_NAME_SIZE); + memcpy(xlo->xlo_crypt_name, info->xlo_crypt_name, XLO_NAME_SIZE); + xlo->xlo_file_name[XLO_NAME_SIZE-1] = 0; + xlo->xlo_crypt_name[XLO_NAME_SIZE-1] = 0; + + if (!xfer) + xfer = &none_funcs; + xlo->transfer = xfer->transfer; + xlo->ioctl = xfer->ioctl; + + xlo->xlo_flags = info->xlo_flags; + + xlo->xlo_encrypt_key_size = info->xlo_encrypt_key_size; + xlo->xlo_init[0] = info->xlo_init[0]; + xlo->xlo_init[1] = info->xlo_init[1]; + if (info->xlo_encrypt_key_size) { + memcpy(xlo->xlo_encrypt_key, info->xlo_encrypt_key, + info->xlo_encrypt_key_size); + xlo->xlo_key_owner = uid; + } + + return 0; +} + +static int xloop_configure(struct xloop_device *xlo, fmode_t mode, + struct block_device *bdev, + const struct xloop_config *config) { struct file *file; struct inode *inode; struct address_space *mapping; struct block_device *claimed_bdev = NULL; - int lo_flags = 0; int error; loff_t size; bool partscan; + unsigned short bsize; /* This is safe, since we have a reference from open(). */ __module_get(THIS_MODULE); error = -EBADF; - file = fget(arg); + file = fget(config->fd); if (!file) goto out; @@ -646,93 +801,119 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, * here to avoid changing device under exclusive owner. */ if (!(mode & FMODE_EXCL)) { - claimed_bdev = bd_start_claiming(bdev, loop_set_fd); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + claimed_bdev = bdev->bd_contains; + error = bd_prepare_to_claim(bdev, claimed_bdev, xloop_configure); + if (error) + goto out_putf; +#else + claimed_bdev = bd_start_claiming(bdev, xloop_configure); if (IS_ERR(claimed_bdev)) { error = PTR_ERR(claimed_bdev); goto out_putf; } +#endif } - error = mutex_lock_killable(&loop_ctl_mutex); + error = mutex_lock_killable(&xloop_ctl_mutex); if (error) goto out_bdev; error = -EBUSY; - if (lo->lo_state != Lo_unbound) + if (xlo->xlo_state != Xlo_unbound) goto out_unlock; - error = loop_validate_file(file, bdev); + error = xloop_validate_file(file, bdev); if (error) goto out_unlock; mapping = file->f_mapping; inode = mapping->host; - if (!(file->f_mode & FMODE_WRITE) || !(mode & FMODE_WRITE) || - !file->f_op->write_iter) - lo_flags |= LO_FLAGS_READ_ONLY; - - set_device_ro(bdev, (lo_flags & LO_FLAGS_READ_ONLY) != 0); - - lo->use_dio = false; - lo->lo_device = bdev; - lo->lo_flags = lo_flags; - lo->lo_backing_file = file; - lo->transfer = NULL; - lo->ioctl = NULL; - lo->lo_sizelimit = 0; - lo->old_gfp_mask = mapping_gfp_mask(mapping); - mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); - - if (!(lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync) - blk_queue_write_cache(lo->lo_queue, true, false); + if ((config->info.xlo_flags & ~XLOOP_CONFIGURE_SETTABLE_FLAGS) != 0) { + error = -EINVAL; + goto out_unlock; + } - loop_update_rotational(lo); - loop_update_dio(lo); + if (config->block_size) { + error = xloop_validate_block_size(config->block_size); + if (error) + goto out_unlock; + } - error = loop_file_fmt_init(lo->lo_fmt, LO_FILE_FMT_RAW); + error = xloop_set_status_from_info(xlo, &config->info); if (error) goto out_unlock; - size = loop_file_fmt_sector_size(lo->lo_fmt); + if (!(file->f_mode & FMODE_WRITE) || !(mode & FMODE_WRITE) || + !file->f_op->write_iter) + xlo->xlo_flags |= XLO_FLAGS_READ_ONLY; - error = -EFBIG; - if ((loff_t)(sector_t)size != size) + error = xloop_prepare_queue(xlo); + if (error) goto out_unlock; - error = loop_prepare_queue(lo); + + error = xloop_file_fmt_init(xlo->xlo_fmt, + config->info.xlo_file_fmt_type); if (error) goto out_unlock; - set_capacity(lo->lo_disk, size); - bd_set_size(bdev, size << 9); - loop_sysfs_init(lo); - /* let user-space know about the new size */ - kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE); + set_device_ro(bdev, (xlo->xlo_flags & XLO_FLAGS_READ_ONLY) != 0); + + xlo->use_dio = xlo->xlo_flags & XLO_FLAGS_DIRECT_IO; + xlo->xlo_device = bdev; + xlo->xlo_backing_file = file; + xlo->old_gfp_mask = mapping_gfp_mask(mapping); + mapping_set_gfp_mask(mapping, xlo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); + + if (!(xlo->xlo_flags & XLO_FLAGS_READ_ONLY) && file->f_op->fsync) + blk_queue_write_cache(xlo->xlo_queue, true, false); + + if (config->block_size) + bsize = config->block_size; + else if ((xlo->xlo_backing_file->f_flags & O_DIRECT) && inode->i_sb->s_bdev) + /* In case of direct I/O, match underlying block size */ + bsize = bdev_logical_block_size(inode->i_sb->s_bdev); + else + bsize = 512; + + blk_queue_logical_block_size(xlo->xlo_queue, bsize); + blk_queue_physical_block_size(xlo->xlo_queue, bsize); + blk_queue_io_min(xlo->xlo_queue, bsize); + + xloop_update_rotational(xlo); + xloop_update_dio(xlo); + xloop_sysfs_init(xlo); + + size = get_xloop_size(xlo, file); + xloop_set_size(xlo, size); set_blocksize(bdev, S_ISBLK(inode->i_mode) ? block_size(inode->i_bdev) : PAGE_SIZE); - lo->lo_state = Lo_bound; + xlo->xlo_state = Xlo_bound; if (part_shift) - lo->lo_flags |= LO_FLAGS_PARTSCAN; - partscan = lo->lo_flags & LO_FLAGS_PARTSCAN; + xlo->xlo_flags |= XLO_FLAGS_PARTSCAN; + partscan = xlo->xlo_flags & XLO_FLAGS_PARTSCAN; + if (partscan) + xlo->xlo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; /* Grab the block_device to prevent its destruction after we - * put /dev/loopXX inode. Later in __loop_clr_fd() we bdput(bdev). + * put /dev/xloopXX inode. Later in __xloop_clr_fd() we bdput(bdev). */ bdgrab(bdev); - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&xloop_ctl_mutex); if (partscan) - loop_reread_partitions(lo, bdev); + xloop_reread_partitions(xlo, bdev); if (claimed_bdev) - bd_abort_claiming(bdev, claimed_bdev, loop_set_fd); + bd_abort_claiming(bdev, claimed_bdev, xloop_configure); return 0; out_unlock: - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&xloop_ctl_mutex); out_bdev: if (claimed_bdev) - bd_abort_claiming(bdev, claimed_bdev, loop_set_fd); + bd_abort_claiming(bdev, claimed_bdev, xloop_configure); out_putf: fput(file); out: @@ -741,94 +922,57 @@ out: return error; } -static int -loop_release_xfer(struct loop_device *lo) -{ - int err = 0; - struct loop_func_table *xfer = lo->lo_encryption; - - if (xfer) { - if (xfer->release) - err = xfer->release(lo); - lo->transfer = NULL; - lo->lo_encryption = NULL; - module_put(xfer->owner); - } - return err; -} - -static int -loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer, - const struct xloop_info64 *i) -{ - int err = 0; - - if (xfer) { - struct module *owner = xfer->owner; - - if (!try_module_get(owner)) - return -EINVAL; - if (xfer->init) - err = xfer->init(lo, i); - if (err) - module_put(owner); - else - lo->lo_encryption = xfer; - } - return err; -} - -static int __loop_clr_fd(struct loop_device *lo, bool release) +static int __xloop_clr_fd(struct xloop_device *xlo, bool release) { struct file *filp = NULL; - gfp_t gfp = lo->old_gfp_mask; - struct block_device *bdev = lo->lo_device; + gfp_t gfp = xlo->old_gfp_mask; + struct block_device *bdev = xlo->xlo_device; int err = 0; bool partscan = false; - int lo_number; + int xlo_number; - mutex_lock(&loop_ctl_mutex); - if (WARN_ON_ONCE(lo->lo_state != Lo_rundown)) { + mutex_lock(&xloop_ctl_mutex); + if (WARN_ON_ONCE(xlo->xlo_state != Xlo_rundown)) { err = -ENXIO; goto out_unlock; } - filp = lo->lo_backing_file; + filp = xlo->xlo_backing_file; if (filp == NULL) { err = -EINVAL; goto out_unlock; } /* freeze request queue during the transition */ - blk_mq_freeze_queue(lo->lo_queue); - - loop_file_fmt_exit(lo->lo_fmt); - - spin_lock_irq(&lo->lo_lock); - lo->lo_backing_file = NULL; - spin_unlock_irq(&lo->lo_lock); - - loop_release_xfer(lo); - lo->transfer = NULL; - lo->ioctl = NULL; - lo->lo_device = NULL; - lo->lo_encryption = NULL; - lo->lo_offset = 0; - lo->lo_sizelimit = 0; - lo->lo_encrypt_key_size = 0; - memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE); - memset(lo->lo_crypt_name, 0, LO_NAME_SIZE); - memset(lo->lo_file_name, 0, LO_NAME_SIZE); - blk_queue_logical_block_size(lo->lo_queue, 512); - blk_queue_physical_block_size(lo->lo_queue, 512); - blk_queue_io_min(lo->lo_queue, 512); + blk_mq_freeze_queue(xlo->xlo_queue); + + xloop_file_fmt_exit(xlo->xlo_fmt); + + spin_lock_irq(&xlo->xlo_lock); + xlo->xlo_backing_file = NULL; + spin_unlock_irq(&xlo->xlo_lock); + + xloop_release_xfer(xlo); + xlo->transfer = NULL; + xlo->ioctl = NULL; + xlo->xlo_device = NULL; + xlo->xlo_encryption = NULL; + xlo->xlo_offset = 0; + xlo->xlo_sizelimit = 0; + xlo->xlo_encrypt_key_size = 0; + memset(xlo->xlo_encrypt_key, 0, XLO_KEY_SIZE); + memset(xlo->xlo_crypt_name, 0, XLO_NAME_SIZE); + memset(xlo->xlo_file_name, 0, XLO_NAME_SIZE); + blk_queue_logical_block_size(xlo->xlo_queue, 512); + blk_queue_physical_block_size(xlo->xlo_queue, 512); + blk_queue_io_min(xlo->xlo_queue, 512); if (bdev) { bdput(bdev); invalidate_bdev(bdev); bdev->bd_inode->i_mapping->wb_err = 0; } - set_capacity(lo->lo_disk, 0); - loop_sysfs_exit(lo); + set_capacity(xlo->xlo_disk, 0); + xloop_sysfs_exit(xlo); if (bdev) { bd_set_size(bdev, 0); /* let user-space know about this change */ @@ -837,541 +981,489 @@ static int __loop_clr_fd(struct loop_device *lo, bool release) mapping_set_gfp_mask(filp->f_mapping, gfp); /* This is safe: open() is still holding a reference. */ module_put(THIS_MODULE); - blk_mq_unfreeze_queue(lo->lo_queue); + blk_mq_unfreeze_queue(xlo->xlo_queue); - partscan = lo->lo_flags & LO_FLAGS_PARTSCAN && bdev; - lo_number = lo->lo_number; - loop_unprepare_queue(lo); + partscan = xlo->xlo_flags & XLO_FLAGS_PARTSCAN && bdev; + xlo_number = xlo->xlo_number; + xloop_unprepare_queue(xlo); out_unlock: - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&xloop_ctl_mutex); if (partscan) { /* * bd_mutex has been held already in release path, so don't * acquire it if this function is called in such case. * - * If the reread partition isn't from release path, lo_refcnt + * If the reread partition isn't from release path, xlo_refcnt * must be at least one and it can only become zero when the * current holder is released. */ - if (release) - err = __blkdev_reread_part(bdev); - else - err = blkdev_reread_part(bdev); + if (!release) + mutex_lock(&bdev->bd_mutex); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0) + err = bdev_disk_changed(bdev, false); +#else + err = blkdev_reread_part(bdev); +#endif + if (!release) + mutex_unlock(&bdev->bd_mutex); if (err) pr_warn("%s: partition scan of xloop%d failed (rc=%d)\n", - __func__, lo_number, err); + __func__, xlo_number, err); /* Device is gone, no point in returning error */ err = 0; } /* - * lo->lo_state is set to Lo_unbound here after above partscan has + * xlo->xlo_state is set to Xlo_unbound here after above partscan has * finished. * - * There cannot be anybody else entering __loop_clr_fd() as - * lo->lo_backing_file is already cleared and Lo_rundown state - * protects us from all the other places trying to change the 'lo' + * There cannot be anybody else entering __xloop_clr_fd() as + * xlo->xlo_backing_file is already cleared and Xlo_rundown state + * protects us from all the other places trying to change the 'xlo' * device. */ - mutex_lock(&loop_ctl_mutex); - lo->lo_flags = 0; + mutex_lock(&xloop_ctl_mutex); + xlo->xlo_flags = 0; if (!part_shift) - lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN; - lo->lo_state = Lo_unbound; - mutex_unlock(&loop_ctl_mutex); + xlo->xlo_disk->flags |= GENHD_FL_NO_PART_SCAN; + xlo->xlo_state = Xlo_unbound; + mutex_unlock(&xloop_ctl_mutex); /* - * Need not hold loop_ctl_mutex to fput backing file. - * Calling fput holding loop_ctl_mutex triggers a circular + * Need not hold xloop_ctl_mutex to fput backing file. + * Calling fput holding xloop_ctl_mutex triggers a circular * lock dependency possibility warning as fput can take - * bd_mutex which is usually taken before loop_ctl_mutex. + * bd_mutex which is usually taken before xloop_ctl_mutex. */ if (filp) fput(filp); return err; } -static int loop_clr_fd(struct loop_device *lo) +static int xloop_clr_fd(struct xloop_device *xlo) { int err; - err = mutex_lock_killable(&loop_ctl_mutex); + err = mutex_lock_killable(&xloop_ctl_mutex); if (err) return err; - if (lo->lo_state != Lo_bound) { - mutex_unlock(&loop_ctl_mutex); + if (xlo->xlo_state != Xlo_bound) { + mutex_unlock(&xloop_ctl_mutex); return -ENXIO; } /* - * If we've explicitly asked to tear down the loop device, + * If we've explicitly asked to tear down the xloop device, * and it has an elevated reference count, set it for auto-teardown when * the last reference goes away. This stops $!~#$@ udev from * preventing teardown because it decided that it needs to run blkid on - * the loopback device whenever they appear. xfstests is notorious for + * the xloopback device whenever they appear. xfstests is notorious for * failing tests because blkid via udev races with a losetup * <dev>/do something like mkfs/losetup -d <dev> causing the losetup -d * command to fail with EBUSY. */ - if (atomic_read(&lo->lo_refcnt) > 1) { - lo->lo_flags |= LO_FLAGS_AUTOCLEAR; - mutex_unlock(&loop_ctl_mutex); + if (atomic_read(&xlo->xlo_refcnt) > 1) { + xlo->xlo_flags |= XLO_FLAGS_AUTOCLEAR; + mutex_unlock(&xloop_ctl_mutex); return 0; } - lo->lo_state = Lo_rundown; - mutex_unlock(&loop_ctl_mutex); + xlo->xlo_state = Xlo_rundown; + mutex_unlock(&xloop_ctl_mutex); - return __loop_clr_fd(lo, false); + return __xloop_clr_fd(xlo, false); } static int -loop_set_status(struct loop_device *lo, const struct xloop_info64 *info) +xloop_set_status(struct xloop_device *xlo, const struct xloop_info64 *info) { int err; - struct loop_func_table *xfer; - kuid_t uid = current_uid(); struct block_device *bdev; + kuid_t uid = current_uid(); + int prev_xlo_flags; bool partscan = false; + bool size_changed = false; - err = mutex_lock_killable(&loop_ctl_mutex); + err = mutex_lock_killable(&xloop_ctl_mutex); if (err) return err; - if (lo->lo_encrypt_key_size && - !uid_eq(lo->lo_key_owner, uid) && + if (xlo->xlo_encrypt_key_size && + !uid_eq(xlo->xlo_key_owner, uid) && !capable(CAP_SYS_ADMIN)) { err = -EPERM; goto out_unlock; } - if (lo->lo_state != Lo_bound) { + if (xlo->xlo_state != Xlo_bound) { err = -ENXIO; goto out_unlock; } - if ((unsigned int) info->lo_encrypt_key_size > LO_KEY_SIZE) { - err = -EINVAL; - goto out_unlock; - } - if (lo->lo_offset != info->lo_offset || - lo->lo_sizelimit != info->lo_sizelimit) { - sync_blockdev(lo->lo_device); - kill_bdev(lo->lo_device); + if (xlo->xlo_offset != info->xlo_offset || + xlo->xlo_sizelimit != info->xlo_sizelimit) { + size_changed = true; + sync_blockdev(xlo->xlo_device); + invalidate_bdev(xlo->xlo_device); } /* I/O need to be drained during transfer transition */ - blk_mq_freeze_queue(lo->lo_queue); + blk_mq_freeze_queue(xlo->xlo_queue); - err = loop_release_xfer(lo); - if (err) + if (size_changed && xlo->xlo_device->bd_inode->i_mapping->nrpages) { + /* If any pages were dirtied after invalidate_bdev(), try again */ + err = -EAGAIN; + pr_warn("%s: xloop%d (%s) has still dirty pages (nrpages=%lu)\n", + __func__, xlo->xlo_number, xlo->xlo_file_name, + xlo->xlo_device->bd_inode->i_mapping->nrpages); goto out_unfreeze; + } - if (info->lo_encrypt_type) { - unsigned int type = info->lo_encrypt_type; - - if (type >= MAX_LO_CRYPT) { - err = -EINVAL; - goto out_unfreeze; - } - xfer = xfer_funcs[type]; - if (xfer == NULL) { - err = -EINVAL; - goto out_unfreeze; - } - } else - xfer = NULL; + prev_xlo_flags = xlo->xlo_flags; - err = loop_init_xfer(lo, xfer, info); + err = xloop_set_status_from_info(xlo, info); if (err) goto out_unfreeze; - if (lo->lo_offset != info->lo_offset || - lo->lo_sizelimit != info->lo_sizelimit) { - /* kill_bdev should have truncated all the pages */ - if (lo->lo_device->bd_inode->i_mapping->nrpages) { - err = -EAGAIN; - pr_warn("%s: xloop%d (%s) has still dirty pages (nrpages=%lu)\n", - __func__, lo->lo_number, lo->lo_file_name, - lo->lo_device->bd_inode->i_mapping->nrpages); - goto out_unfreeze; - } - if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) { - err = -EFBIG; - goto out_unfreeze; - } - } + /* Mask out flags that can't be set using XLOOP_SET_STATUS. */ + xlo->xlo_flags &= XLOOP_SET_STATUS_SETTABLE_FLAGS; + /* For those flags, use the previous values instead */ + xlo->xlo_flags |= prev_xlo_flags & ~XLOOP_SET_STATUS_SETTABLE_FLAGS; + /* For flags that can't be cleared, use previous values too */ + xlo->xlo_flags |= prev_xlo_flags & ~XLOOP_SET_STATUS_CLEARABLE_FLAGS; - if (lo->lo_fmt->file_fmt_type != info->lo_file_fmt_type) { - err = loop_file_fmt_change(lo->lo_fmt, info->lo_file_fmt_type); + if (xlo->xlo_fmt->file_fmt_type != info->xlo_file_fmt_type) { + /* xloop file format has changed, so change file format driver */ + err = xloop_file_fmt_change(xlo->xlo_fmt, info->xlo_file_fmt_type); if (err) goto out_unfreeze; /* After change of the file format, recalculate the capacity of - * the loop device. figure_loop_size() automatically calls the - * sector_size function of the corresponding loop file format - * driver to determine the new capacity. */ - if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) { - err = -EFBIG; - goto out_unfreeze; - } + * the loop device. */ + size_changed = true; } - loop_config_discard(lo); - - memcpy(lo->lo_file_name, info->lo_file_name, LO_NAME_SIZE); - memcpy(lo->lo_crypt_name, info->lo_crypt_name, LO_NAME_SIZE); - lo->lo_file_name[LO_NAME_SIZE-1] = 0; - lo->lo_crypt_name[LO_NAME_SIZE-1] = 0; - - if (!xfer) - xfer = &none_funcs; - lo->transfer = xfer->transfer; - lo->ioctl = xfer->ioctl; - - if ((lo->lo_flags & LO_FLAGS_AUTOCLEAR) != - (info->lo_flags & LO_FLAGS_AUTOCLEAR)) - lo->lo_flags ^= LO_FLAGS_AUTOCLEAR; - - lo->lo_encrypt_key_size = info->lo_encrypt_key_size; - lo->lo_init[0] = info->lo_init[0]; - lo->lo_init[1] = info->lo_init[1]; - if (info->lo_encrypt_key_size) { - memcpy(lo->lo_encrypt_key, info->lo_encrypt_key, - info->lo_encrypt_key_size); - lo->lo_key_owner = uid; + if (size_changed) { + loff_t new_size = get_xloop_size(xlo, xlo->xlo_backing_file); + xloop_set_size(xlo, new_size); } - /* update dio if lo_offset or transfer is changed */ - __loop_update_dio(lo, lo->use_dio); + xloop_config_discard(xlo); + + /* update dio if xlo_offset or transfer is changed */ + __xloop_update_dio(xlo, xlo->use_dio); out_unfreeze: - blk_mq_unfreeze_queue(lo->lo_queue); + blk_mq_unfreeze_queue(xlo->xlo_queue); - if (!err && (info->lo_flags & LO_FLAGS_PARTSCAN) && - !(lo->lo_flags & LO_FLAGS_PARTSCAN)) { - lo->lo_flags |= LO_FLAGS_PARTSCAN; - lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; - bdev = lo->lo_device; + if (!err && (xlo->xlo_flags & XLO_FLAGS_PARTSCAN) && + !(prev_xlo_flags & XLO_FLAGS_PARTSCAN)) { + xlo->xlo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; + bdev = xlo->xlo_device; partscan = true; } out_unlock: - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&xloop_ctl_mutex); if (partscan) - loop_reread_partitions(lo, bdev); + xloop_reread_partitions(xlo, bdev); return err; } static int -loop_get_status(struct loop_device *lo, struct xloop_info64 *info) +xloop_get_status(struct xloop_device *xlo, struct xloop_info64 *info) { struct path path; struct kstat stat; int ret; - ret = mutex_lock_killable(&loop_ctl_mutex); + ret = mutex_lock_killable(&xloop_ctl_mutex); if (ret) return ret; - if (lo->lo_state != Lo_bound) { - mutex_unlock(&loop_ctl_mutex); + if (xlo->xlo_state != Xlo_bound) { + mutex_unlock(&xloop_ctl_mutex); return -ENXIO; } memset(info, 0, sizeof(*info)); - info->lo_number = lo->lo_number; - info->lo_offset = lo->lo_offset; - info->lo_sizelimit = lo->lo_sizelimit; - info->lo_flags = lo->lo_flags; - memcpy(info->lo_file_name, lo->lo_file_name, LO_NAME_SIZE); - memcpy(info->lo_crypt_name, lo->lo_crypt_name, LO_NAME_SIZE); - info->lo_encrypt_type = - lo->lo_encryption ? lo->lo_encryption->number : 0; - if (lo->lo_encrypt_key_size && capable(CAP_SYS_ADMIN)) { - info->lo_encrypt_key_size = lo->lo_encrypt_key_size; - memcpy(info->lo_encrypt_key, lo->lo_encrypt_key, - lo->lo_encrypt_key_size); + info->xlo_number = xlo->xlo_number; + info->xlo_offset = xlo->xlo_offset; + info->xlo_sizelimit = xlo->xlo_sizelimit; + info->xlo_flags = xlo->xlo_flags; + memcpy(info->xlo_file_name, xlo->xlo_file_name, XLO_NAME_SIZE); + memcpy(info->xlo_crypt_name, xlo->xlo_crypt_name, XLO_NAME_SIZE); + info->xlo_encrypt_type = + xlo->xlo_encryption ? xlo->xlo_encryption->number : 0; + if (xlo->xlo_encrypt_key_size && capable(CAP_SYS_ADMIN)) { + info->xlo_encrypt_key_size = xlo->xlo_encrypt_key_size; + memcpy(info->xlo_encrypt_key, xlo->xlo_encrypt_key, + xlo->xlo_encrypt_key_size); } - info->lo_file_fmt_type = lo->lo_fmt->file_fmt_type; - /* Drop loop_ctl_mutex while we call into the filesystem. */ - path = lo->lo_backing_file->f_path; + /* Drop xloop_ctl_mutex while we call into the filesystem. */ + path = xlo->xlo_backing_file->f_path; path_get(&path); - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&xloop_ctl_mutex); ret = vfs_getattr(&path, &stat, STATX_INO, AT_STATX_SYNC_AS_STAT); if (!ret) { - info->lo_device = huge_encode_dev(stat.dev); - info->lo_inode = stat.ino; - info->lo_rdevice = huge_encode_dev(stat.rdev); + info->xlo_device = huge_encode_dev(stat.dev); + info->xlo_inode = stat.ino; + info->xlo_rdevice = huge_encode_dev(stat.rdev); } path_put(&path); return ret; } static void -loop_info64_from_old(const struct xloop_info *info, struct xloop_info64 *info64) +xloop_info64_from_old(const struct xloop_info *info, struct xloop_info64 *info64) { memset(info64, 0, sizeof(*info64)); - info64->lo_number = info->lo_number; - info64->lo_device = info->lo_device; - info64->lo_inode = info->lo_inode; - info64->lo_rdevice = info->lo_rdevice; - info64->lo_offset = info->lo_offset; - info64->lo_sizelimit = 0; - info64->lo_encrypt_type = info->lo_encrypt_type; - info64->lo_encrypt_key_size = info->lo_encrypt_key_size; - info64->lo_flags = info->lo_flags; - info64->lo_init[0] = info->lo_init[0]; - info64->lo_init[1] = info->lo_init[1]; - info64->lo_file_fmt_type = info->lo_file_fmt_type; - if (info->lo_encrypt_type == LO_CRYPT_CRYPTOAPI) - memcpy(info64->lo_crypt_name, info->lo_name, LO_NAME_SIZE); + info64->xlo_number = info->xlo_number; + info64->xlo_device = info->xlo_device; + info64->xlo_inode = info->xlo_inode; + info64->xlo_rdevice = info->xlo_rdevice; + info64->xlo_offset = info->xlo_offset; + info64->xlo_sizelimit = 0; + info64->xlo_encrypt_type = info->xlo_encrypt_type; + info64->xlo_encrypt_key_size = info->xlo_encrypt_key_size; + info64->xlo_flags = info->xlo_flags; + info64->xlo_init[0] = info->xlo_init[0]; + info64->xlo_init[1] = info->xlo_init[1]; + info64->xlo_file_fmt_type = info->xlo_file_fmt_type; + if (info->xlo_encrypt_type == XLO_CRYPT_CRYPTOAPI) + memcpy(info64->xlo_crypt_name, info->xlo_name, XLO_NAME_SIZE); else - memcpy(info64->lo_file_name, info->lo_name, LO_NAME_SIZE); - memcpy(info64->lo_encrypt_key, info->lo_encrypt_key, LO_KEY_SIZE); + memcpy(info64->xlo_file_name, info->xlo_name, XLO_NAME_SIZE); + memcpy(info64->xlo_encrypt_key, info->xlo_encrypt_key, XLO_KEY_SIZE); } static int -loop_info64_to_old(const struct xloop_info64 *info64, struct xloop_info *info) +xloop_info64_to_old(const struct xloop_info64 *info64, struct xloop_info *info) { memset(info, 0, sizeof(*info)); - info->lo_number = info64->lo_number; - info->lo_device = info64->lo_device; - info->lo_inode = info64->lo_inode; - info->lo_rdevice = info64->lo_rdevice; - info->lo_offset = info64->lo_offset; - info->lo_encrypt_type = info64->lo_encrypt_type; - info->lo_encrypt_key_size = info64->lo_encrypt_key_size; - info->lo_flags = info64->lo_flags; - info->lo_init[0] = info64->lo_init[0]; - info->lo_init[1] = info64->lo_init[1]; - info->lo_file_fmt_type = info64->lo_file_fmt_type; - if (info->lo_encrypt_type == LO_CRYPT_CRYPTOAPI) - memcpy(info->lo_name, info64->lo_crypt_name, LO_NAME_SIZE); + info->xlo_number = info64->xlo_number; + info->xlo_device = info64->xlo_device; + info->xlo_inode = info64->xlo_inode; + info->xlo_rdevice = info64->xlo_rdevice; + info->xlo_offset = info64->xlo_offset; + info->xlo_encrypt_type = info64->xlo_encrypt_type; + info->xlo_encrypt_key_size = info64->xlo_encrypt_key_size; + info->xlo_flags = info64->xlo_flags; + info->xlo_init[0] = info64->xlo_init[0]; + info->xlo_init[1] = info64->xlo_init[1]; + info->xlo_file_fmt_type = info64->xlo_file_fmt_type; + if (info->xlo_encrypt_type == XLO_CRYPT_CRYPTOAPI) + memcpy(info->xlo_name, info64->xlo_crypt_name, XLO_NAME_SIZE); else - memcpy(info->lo_name, info64->lo_file_name, LO_NAME_SIZE); - memcpy(info->lo_encrypt_key, info64->lo_encrypt_key, LO_KEY_SIZE); + memcpy(info->xlo_name, info64->xlo_file_name, XLO_NAME_SIZE); + memcpy(info->xlo_encrypt_key, info64->xlo_encrypt_key, XLO_KEY_SIZE); /* error in case values were truncated */ - if (info->lo_device != info64->lo_device || - info->lo_rdevice != info64->lo_rdevice || - info->lo_inode != info64->lo_inode || - info->lo_offset != info64->lo_offset) + if (info->xlo_device != info64->xlo_device || + info->xlo_rdevice != info64->xlo_rdevice || + info->xlo_inode != info64->xlo_inode || + info->xlo_offset != info64->xlo_offset) return -EOVERFLOW; return 0; } static int -loop_set_status_old(struct loop_device *lo, const struct xloop_info __user *arg) +xloop_set_status_old(struct xloop_device *xlo, const struct xloop_info __user *arg) { struct xloop_info info; struct xloop_info64 info64; - int err; - /* copy everything from the user space */ - err = copy_from_user(&info, arg, sizeof(info)); - if (err) + if (copy_from_user(&info, arg, sizeof (struct xloop_info))) return -EFAULT; - - loop_info64_from_old(&info, &info64); - return loop_set_status(lo, &info64); + xloop_info64_from_old(&info, &info64); + return xloop_set_status(xlo, &info64); } static int -loop_set_status64(struct loop_device *lo, const struct xloop_info64 __user *arg) +xloop_set_status64(struct xloop_device *xlo, const struct xloop_info64 __user *arg) { struct xloop_info64 info64; - int err; - /* copy everything from the user space */ - err = copy_from_user(&info64, arg, sizeof(info64)); - if (err) + if (copy_from_user(&info64, arg, sizeof (struct xloop_info64))) return -EFAULT; - - return loop_set_status(lo, &info64); + return xloop_set_status(xlo, &info64); } static int -loop_get_status_old(struct loop_device *lo, struct xloop_info __user *arg) { +xloop_get_status_old(struct xloop_device *xlo, struct xloop_info __user *arg) { struct xloop_info info; struct xloop_info64 info64; - int lo_flags; int err; if (!arg) return -EINVAL; - - /* backward compatibility: copy everything except the file format type - * field */ - err = copy_from_user(&info, arg, - sizeof(info) - sizeof(info.lo_file_fmt_type)); - if (err) - return -EFAULT; - - lo_flags = info.lo_flags; - - err = loop_get_status(lo, &info64); + err = xloop_get_status(xlo, &info64); if (!err) - err = loop_info64_to_old(&info64, &info); - - err = copy_to_user(arg, &info, sizeof(info)); - - if (err) - return -EFAULT; + err = xloop_info64_to_old(&info64, &info); + if (!err && copy_to_user(arg, &info, sizeof(info))) + err = -EFAULT; return err; } static int -loop_get_status64(struct loop_device *lo, struct xloop_info64 __user *arg) { +xloop_get_status64(struct xloop_device *xlo, struct xloop_info64 __user *arg) { struct xloop_info64 info64; - u32 lo_flags; int err; if (!arg) return -EINVAL; - - /* backward compatibility: copy everything except the file format type - * field */ - err = copy_from_user(&info64, arg, - sizeof(info64) - sizeof(info64.lo_file_fmt_type)); - if (err) - return -EFAULT; - - lo_flags = info64.lo_flags; - - err = loop_get_status(lo, &info64); - if (err) - return -EFAULT; - - err = copy_to_user(arg, &info64, sizeof(info64)); - - if (err) - return -EFAULT; + err = xloop_get_status(xlo, &info64); + if (!err && copy_to_user(arg, &info64, sizeof(info64))) + err = -EFAULT; return err; } -static int loop_set_capacity(struct loop_device *lo) +static int xloop_set_capacity(struct xloop_device *xlo) { - if (unlikely(lo->lo_state != Lo_bound)) + loff_t size; + + if (unlikely(xlo->xlo_state != Xlo_bound)) return -ENXIO; - return figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit); + size = get_xloop_size(xlo, xlo->xlo_backing_file); + xloop_set_size(xlo, size); + + return 0; } -static int loop_set_dio(struct loop_device *lo, unsigned long arg) +static int xloop_set_dio(struct xloop_device *xlo, unsigned long arg) { int error = -ENXIO; - if (lo->lo_state != Lo_bound) + if (xlo->xlo_state != Xlo_bound) goto out; - __loop_update_dio(lo, !!arg); - if (lo->use_dio == !!arg) + __xloop_update_dio(xlo, !!arg); + if (xlo->use_dio == !!arg) return 0; error = -EINVAL; out: return error; } -static int loop_set_block_size(struct loop_device *lo, unsigned long arg) +static int xloop_set_block_size(struct xloop_device *xlo, unsigned long arg) { int err = 0; - if (lo->lo_state != Lo_bound) + if (xlo->xlo_state != Xlo_bound) return -ENXIO; - if (arg < 512 || arg > PAGE_SIZE || !is_power_of_2(arg)) - return -EINVAL; + err = xloop_validate_block_size(arg); + if (err) + return err; - if (lo->lo_queue->limits.logical_block_size != arg) { - sync_blockdev(lo->lo_device); - kill_bdev(lo->lo_device); - } + if (xlo->xlo_queue->limits.logical_block_size == arg) + return 0; - blk_mq_freeze_queue(lo->lo_queue); + sync_blockdev(xlo->xlo_device); + invalidate_bdev(xlo->xlo_device); - /* kill_bdev should have truncated all the pages */ - if (lo->lo_queue->limits.logical_block_size != arg && - lo->lo_device->bd_inode->i_mapping->nrpages) { + blk_mq_freeze_queue(xlo->xlo_queue); + + /* invalidate_bdev should have truncated all the pages */ + if (xlo->xlo_device->bd_inode->i_mapping->nrpages) { err = -EAGAIN; pr_warn("%s: xloop%d (%s) has still dirty pages (nrpages=%lu)\n", - __func__, lo->lo_number, lo->lo_file_name, - lo->lo_device->bd_inode->i_mapping->nrpages); + __func__, xlo->xlo_number, xlo->xlo_file_name, + xlo->xlo_device->bd_inode->i_mapping->nrpages); goto out_unfreeze; } - blk_queue_logical_block_size(lo->lo_queue, arg); - blk_queue_physical_block_size(lo->lo_queue, arg); - blk_queue_io_min(lo->lo_queue, arg); - loop_update_dio(lo); + blk_queue_logical_block_size(xlo->xlo_queue, arg); + blk_queue_physical_block_size(xlo->xlo_queue, arg); + blk_queue_io_min(xlo->xlo_queue, arg); + xloop_update_dio(xlo); out_unfreeze: - blk_mq_unfreeze_queue(lo->lo_queue); + blk_mq_unfreeze_queue(xlo->xlo_queue); return err; } -static int lo_simple_ioctl(struct loop_device *lo, unsigned int cmd, +static int xlo_simple_ioctl(struct xloop_device *xlo, unsigned int cmd, unsigned long arg) { int err; - err = mutex_lock_killable(&loop_ctl_mutex); + err = mutex_lock_killable(&xloop_ctl_mutex); if (err) return err; switch (cmd) { - case LOOP_SET_CAPACITY: - err = loop_set_capacity(lo); + case XLOOP_SET_CAPACITY: + err = xloop_set_capacity(xlo); break; - case LOOP_SET_DIRECT_IO: - err = loop_set_dio(lo, arg); + case XLOOP_SET_DIRECT_IO: + err = xloop_set_dio(xlo, arg); break; - case LOOP_SET_BLOCK_SIZE: - err = loop_set_block_size(lo, arg); + case XLOOP_SET_BLOCK_SIZE: + err = xloop_set_block_size(xlo, arg); break; default: - err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL; + err = xlo->ioctl ? xlo->ioctl(xlo, cmd, arg) : -EINVAL; } - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&xloop_ctl_mutex); return err; } -static int lo_ioctl(struct block_device *bdev, fmode_t mode, +static int xlo_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { - struct loop_device *lo = bdev->bd_disk->private_data; + struct xloop_device *xlo = bdev->bd_disk->private_data; + void __user *argp = (void __user *) arg; int err; switch (cmd) { - case LOOP_SET_FD: - return loop_set_fd(lo, mode, bdev, arg); - case LOOP_CHANGE_FD: - return loop_change_fd(lo, bdev, arg); - case LOOP_CLR_FD: - return loop_clr_fd(lo); - case LOOP_SET_STATUS: + case XLOOP_SET_FD: { + /* + * Legacy case - pass in a zeroed out struct xloop_config with + * only the file descriptor set , which corresponds with the + * default parameters we'd have used otherwise. + */ + struct xloop_config config; + + memset(&config, 0, sizeof(config)); + config.fd = arg; + + return xloop_configure(xlo, mode, bdev, &config); + } + case XLOOP_CONFIGURE: { + struct xloop_config config; + + if (copy_from_user(&config, argp, sizeof(config))) + return -EFAULT; + + return xloop_configure(xlo, mode, bdev, &config); + } + case XLOOP_CHANGE_FD: + return xloop_change_fd(xlo, bdev, arg); + case XLOOP_CLR_FD: + return xloop_clr_fd(xlo); + case XLOOP_SET_STATUS: err = -EPERM; if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) { - err = loop_set_status_old(lo, - (struct xloop_info __user *)arg); + err = xloop_set_status_old(xlo, argp); } break; - case LOOP_GET_STATUS: - return loop_get_status_old(lo, (struct xloop_info __user *) arg); - case LOOP_SET_STATUS64: + case XLOOP_GET_STATUS: + return xloop_get_status_old(xlo, argp); + case XLOOP_SET_STATUS64: err = -EPERM; if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) { - err = loop_set_status64(lo, - (struct xloop_info64 __user *) arg); + err = xloop_set_status64(xlo, argp); } break; - case LOOP_GET_STATUS64: - return loop_get_status64(lo, (struct xloop_info64 __user *) arg); - case LOOP_SET_CAPACITY: - case LOOP_SET_DIRECT_IO: - case LOOP_SET_BLOCK_SIZE: + case XLOOP_GET_STATUS64: + return xloop_get_status64(xlo, argp); + case XLOOP_SET_CAPACITY: + case XLOOP_SET_DIRECT_IO: + case XLOOP_SET_BLOCK_SIZE: if (!(mode & FMODE_WRITE) && !capable(CAP_SYS_ADMIN)) return -EPERM; - /* Fall through */ + fallthrough; default: - err = lo_simple_ioctl(lo, cmd, arg); + err = xlo_simple_ioctl(xlo, cmd, arg); break; } @@ -1379,169 +1471,152 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, } #ifdef CONFIG_COMPAT -struct compat_loop_info { - compat_int_t lo_number; /* ioctl r/o */ - compat_dev_t lo_device; /* ioctl r/o */ - compat_ulong_t lo_inode; /* ioctl r/o */ - compat_dev_t lo_rdevice; /* ioctl r/o */ - compat_int_t lo_offset; - compat_int_t lo_encrypt_type; - compat_int_t lo_encrypt_key_size; /* ioctl w/o */ - compat_int_t lo_flags; /* ioctl r/o */ - char lo_name[LO_NAME_SIZE]; - unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ - compat_ulong_t lo_init[2]; +struct compat_xloop_info { + compat_int_t xlo_number; /* ioctl r/o */ + compat_dev_t xlo_device; /* ioctl r/o */ + compat_ulong_t xlo_inode; /* ioctl r/o */ + compat_dev_t xlo_rdevice; /* ioctl r/o */ + compat_int_t xlo_offset; + compat_int_t xlo_encrypt_type; + compat_int_t xlo_encrypt_key_size; /* ioctl w/o */ + compat_int_t xlo_flags; /* ioctl r/o */ + char xlo_name[XLO_NAME_SIZE]; + unsigned char xlo_encrypt_key[XLO_KEY_SIZE]; /* ioctl w/o */ + compat_ulong_t xlo_init[2]; char reserved[4]; - compat_int_t lo_file_fmt_type; -} __attribute__((packed)); + compat_int_t xlo_file_fmt_type; +}; /* - * Transfer 32-bit compatibility structure in userspace to 64-bit loop info + * Transfer 32-bit compatibility structure in userspace to 64-bit xloop info * - noinlined to reduce stack space usage in main part of driver */ static noinline int -loop_info64_from_compat(const struct compat_loop_info __user *arg, +xloop_info64_from_compat(const struct compat_xloop_info __user *arg, struct xloop_info64 *info64) { - struct compat_loop_info info; - int err; + struct compat_xloop_info info; - /* backward compatibility: copy everything except the file format type - * field */ - err = copy_from_user(&info, arg, sizeof(info)); - if (err) + if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; memset(info64, 0, sizeof(*info64)); - info64->lo_number = info.lo_number; - info64->lo_device = info.lo_device; - info64->lo_inode = info.lo_inode; - info64->lo_rdevice = info.lo_rdevice; - info64->lo_offset = info.lo_offset; - info64->lo_sizelimit = 0; - info64->lo_encrypt_type = info.lo_encrypt_type; - info64->lo_encrypt_key_size = info.lo_encrypt_key_size; - info64->lo_flags = info.lo_flags; - info64->lo_init[0] = info.lo_init[0]; - info64->lo_init[1] = info.lo_init[1]; - info64->lo_file_fmt_type = info.lo_file_fmt_type; - if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI) - memcpy(info64->lo_crypt_name, info.lo_name, LO_NAME_SIZE); + info64->xlo_number = info.xlo_number; + info64->xlo_device = info.xlo_device; + info64->xlo_inode = info.xlo_inode; + info64->xlo_rdevice = info.xlo_rdevice; + info64->xlo_offset = info.xlo_offset; + info64->xlo_sizelimit = 0; + info64->xlo_encrypt_type = info.xlo_encrypt_type; + info64->xlo_encrypt_key_size = info.xlo_encrypt_key_size; + info64->xlo_flags = info.xlo_flags; + info64->xlo_init[0] = info.xlo_init[0]; + info64->xlo_init[1] = info.xlo_init[1]; + info64->xlo_file_fmt_type = info.xlo_file_fmt_type; + if (info.xlo_encrypt_type == XLO_CRYPT_CRYPTOAPI) + memcpy(info64->xlo_crypt_name, info.xlo_name, XLO_NAME_SIZE); else - memcpy(info64->lo_file_name, info.lo_name, LO_NAME_SIZE); - memcpy(info64->lo_encrypt_key, info.lo_encrypt_key, LO_KEY_SIZE); + memcpy(info64->xlo_file_name, info.xlo_name, XLO_NAME_SIZE); + memcpy(info64->xlo_encrypt_key, info.xlo_encrypt_key, XLO_KEY_SIZE); return 0; } /* - * Transfer 64-bit loop info to 32-bit compatibility structure in userspace + * Transfer 64-bit xloop info to 32-bit compatibility structure in userspace * - noinlined to reduce stack space usage in main part of driver */ static noinline int -loop_info64_to_compat(const struct xloop_info64 *info64, - struct compat_loop_info __user *arg) +xloop_info64_to_compat(const struct xloop_info64 *info64, + struct compat_xloop_info __user *arg) { - struct compat_loop_info info; - compat_int_t lo_flags; - int err; - - /* backward compatibility: copy everything except the file format type - * field */ - err = copy_from_user(&info, arg, - sizeof(info) - sizeof(info.lo_file_fmt_type)); - if (err) - return -EFAULT; - - lo_flags = info.lo_flags; + struct compat_xloop_info info; memset(&info, 0, sizeof(info)); - info.lo_number = info64->lo_number; - info.lo_device = info64->lo_device; - info.lo_inode = info64->lo_inode; - info.lo_rdevice = info64->lo_rdevice; - info.lo_offset = info64->lo_offset; - info.lo_encrypt_type = info64->lo_encrypt_type; - info.lo_encrypt_key_size = info64->lo_encrypt_key_size; - info.lo_flags = info64->lo_flags; - info.lo_init[0] = info64->lo_init[0]; - info.lo_init[1] = info64->lo_init[1]; - info.lo_file_fmt_type = info64->lo_file_fmt_type; - if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI) - memcpy(info.lo_name, info64->lo_crypt_name, LO_NAME_SIZE); + info.xlo_number = info64->xlo_number; + info.xlo_device = info64->xlo_device; + info.xlo_inode = info64->xlo_inode; + info.xlo_rdevice = info64->xlo_rdevice; + info.xlo_offset = info64->xlo_offset; + info.xlo_encrypt_type = info64->xlo_encrypt_type; + info.xlo_encrypt_key_size = info64->xlo_encrypt_key_size; + info.xlo_flags = info64->xlo_flags; + info.xlo_init[0] = info64->xlo_init[0]; + info.xlo_init[1] = info64->xlo_init[1]; + if (info.xlo_encrypt_type == XLO_CRYPT_CRYPTOAPI) + memcpy(info.xlo_name, info64->xlo_crypt_name, XLO_NAME_SIZE); else - memcpy(info.lo_name, info64->lo_file_name, LO_NAME_SIZE); - memcpy(info.lo_encrypt_key, info64->lo_encrypt_key, LO_KEY_SIZE); + memcpy(info.xlo_name, info64->xlo_file_name, XLO_NAME_SIZE); + memcpy(info.xlo_encrypt_key, info64->xlo_encrypt_key, XLO_KEY_SIZE); /* error in case values were truncated */ - if (info.lo_device != info64->lo_device || - info.lo_rdevice != info64->lo_rdevice || - info.lo_inode != info64->lo_inode || - info.lo_offset != info64->lo_offset || - info.lo_init[0] != info64->lo_init[0] || - info.lo_init[1] != info64->lo_init[1] || - info.lo_file_fmt_type != info64->lo_file_fmt_type) + if (info.xlo_device != info64->xlo_device || + info.xlo_rdevice != info64->xlo_rdevice || + info.xlo_inode != info64->xlo_inode || + info.xlo_offset != info64->xlo_offset || + info.xlo_init[0] != info64->xlo_init[0] || + info.xlo_init[1] != info64->xlo_init[1]) return -EOVERFLOW; - err = copy_to_user(arg, &info, sizeof(info)); - - if (err) + if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; return 0; } static int -loop_set_status_compat(struct loop_device *lo, - const struct compat_loop_info __user *arg) +xloop_set_status_compat(struct xloop_device *xlo, + const struct compat_xloop_info __user *arg) { struct xloop_info64 info64; int ret; - ret = loop_info64_from_compat(arg, &info64); + ret = xloop_info64_from_compat(arg, &info64); if (ret < 0) return ret; - return loop_set_status(lo, &info64); + return xloop_set_status(xlo, &info64); } static int -loop_get_status_compat(struct loop_device *lo, - struct compat_loop_info __user *arg) +xloop_get_status_compat(struct xloop_device *xlo, + struct compat_xloop_info __user *arg) { struct xloop_info64 info64; int err; if (!arg) return -EINVAL; - err = loop_get_status(lo, &info64); + err = xloop_get_status(xlo, &info64); if (!err) - err = loop_info64_to_compat(&info64, arg); + err = xloop_info64_to_compat(&info64, arg); return err; } -static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode, +static int xlo_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { - struct loop_device *lo = bdev->bd_disk->private_data; + struct xloop_device *xlo = bdev->bd_disk->private_data; int err; switch(cmd) { - case LOOP_SET_STATUS: - err = loop_set_status_compat(lo, - (const struct compat_loop_info __user *)arg); + case XLOOP_SET_STATUS: + err = xloop_set_status_compat(xlo, + (const struct compat_xloop_info __user *)arg); break; - case LOOP_GET_STATUS: - err = loop_get_status_compat(lo, - (struct compat_loop_info __user *)arg); + case XLOOP_GET_STATUS: + err = xloop_get_status_compat(xlo, + (struct compat_xloop_info __user *)arg); break; - case LOOP_SET_CAPACITY: - case LOOP_CLR_FD: - case LOOP_GET_STATUS64: - case LOOP_SET_STATUS64: + case XLOOP_SET_CAPACITY: + case XLOOP_CLR_FD: + case XLOOP_GET_STATUS64: + case XLOOP_SET_STATUS64: + case XLOOP_CONFIGURE: arg = (unsigned long) compat_ptr(arg); - /* fall through */ - case LOOP_SET_FD: - case LOOP_CHANGE_FD: - case LOOP_SET_BLOCK_SIZE: - err = lo_ioctl(bdev, mode, cmd, arg); + fallthrough; + case XLOOP_SET_FD: + case XLOOP_CHANGE_FD: + case XLOOP_SET_BLOCK_SIZE: + case XLOOP_SET_DIRECT_IO: + err = xlo_ioctl(bdev, mode, cmd, arg); break; default: err = -ENOIOCTLCMD; @@ -1551,85 +1626,85 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode, } #endif -static int lo_open(struct block_device *bdev, fmode_t mode) +static int xlo_open(struct block_device *bdev, fmode_t mode) { - struct loop_device *lo; + struct xloop_device *xlo; int err; - err = mutex_lock_killable(&loop_ctl_mutex); + err = mutex_lock_killable(&xloop_ctl_mutex); if (err) return err; - lo = bdev->bd_disk->private_data; - if (!lo) { + xlo = bdev->bd_disk->private_data; + if (!xlo) { err = -ENXIO; goto out; } - atomic_inc(&lo->lo_refcnt); + atomic_inc(&xlo->xlo_refcnt); out: - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&xloop_ctl_mutex); return err; } -static void lo_release(struct gendisk *disk, fmode_t mode) +static void xlo_release(struct gendisk *disk, fmode_t mode) { - struct loop_device *lo; + struct xloop_device *xlo; - mutex_lock(&loop_ctl_mutex); - lo = disk->private_data; - if (atomic_dec_return(&lo->lo_refcnt)) + mutex_lock(&xloop_ctl_mutex); + xlo = disk->private_data; + if (atomic_dec_return(&xlo->xlo_refcnt)) goto out_unlock; - if (lo->lo_flags & LO_FLAGS_AUTOCLEAR) { - if (lo->lo_state != Lo_bound) + if (xlo->xlo_flags & XLO_FLAGS_AUTOCLEAR) { + if (xlo->xlo_state != Xlo_bound) goto out_unlock; - lo->lo_state = Lo_rundown; - mutex_unlock(&loop_ctl_mutex); + xlo->xlo_state = Xlo_rundown; + mutex_unlock(&xloop_ctl_mutex); /* - * In autoclear mode, stop the loop thread + * In autoclear mode, stop the xloop thread * and remove configuration after last close. */ - __loop_clr_fd(lo, true); + __xloop_clr_fd(xlo, true); return; - } else if (lo->lo_state == Lo_bound) { + } else if (xlo->xlo_state == Xlo_bound) { /* * Otherwise keep thread (if running) and config, * but flush possible ongoing bios in thread. */ - blk_mq_freeze_queue(lo->lo_queue); - blk_mq_unfreeze_queue(lo->lo_queue); + blk_mq_freeze_queue(xlo->xlo_queue); + blk_mq_unfreeze_queue(xlo->xlo_queue); } out_unlock: - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&xloop_ctl_mutex); } -static const struct block_device_operations lo_fops = { +static const struct block_device_operations xlo_fops = { .owner = THIS_MODULE, - .open = lo_open, - .release = lo_release, - .ioctl = lo_ioctl, + .open = xlo_open, + .release = xlo_release, + .ioctl = xlo_ioctl, #ifdef CONFIG_COMPAT - .compat_ioctl = lo_compat_ioctl, + .compat_ioctl = xlo_compat_ioctl, #endif }; /* * And now the modules code and kernel interface. */ -static int max_loop; -module_param(max_loop, int, 0444); -MODULE_PARM_DESC(max_loop, "Maximum number of loop devices"); +static int max_xloop; +module_param(max_xloop, int, 0444); +MODULE_PARM_DESC(max_xloop, "Maximum number of xloop devices"); module_param(max_part, int, 0444); -MODULE_PARM_DESC(max_part, "Maximum number of partitions per loop device"); +MODULE_PARM_DESC(max_part, "Maximum number of partitions per xloop device"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_BLOCKDEV_MAJOR(XLOOP_MAJOR); +MODULE_ALIAS_BLOCKDEV_MAJOR(LOOP_MAJOR); -int xloop_register_transfer(struct loop_func_table *funcs) +int xloop_register_transfer(struct xloop_func_table *funcs) { unsigned int n = funcs->number; - if (n >= MAX_LO_CRYPT || xfer_funcs[n]) + if (n >= MAX_XLO_CRYPT || xfer_funcs[n]) return -EINVAL; xfer_funcs[n] = funcs; return 0; @@ -1637,42 +1712,42 @@ int xloop_register_transfer(struct loop_func_table *funcs) static int unregister_transfer_cb(int id, void *ptr, void *data) { - struct loop_device *lo = ptr; - struct loop_func_table *xfer = data; + struct xloop_device *xlo = ptr; + struct xloop_func_table *xfer = data; - mutex_lock(&loop_ctl_mutex); - if (lo->lo_encryption == xfer) - loop_release_xfer(lo); - mutex_unlock(&loop_ctl_mutex); + mutex_lock(&xloop_ctl_mutex); + if (xlo->xlo_encryption == xfer) + xloop_release_xfer(xlo); + mutex_unlock(&xloop_ctl_mutex); return 0; } int xloop_unregister_transfer(int number) { unsigned int n = number; - struct loop_func_table *xfer; + struct xloop_func_table *xfer; - if (n == 0 || n >= MAX_LO_CRYPT || (xfer = xfer_funcs[n]) == NULL) + if (n == 0 || n >= MAX_XLO_CRYPT || (xfer = xfer_funcs[n]) == NULL) return -EINVAL; xfer_funcs[n] = NULL; - idr_for_each(&loop_index_idr, &unregister_transfer_cb, xfer); + idr_for_each(&xloop_index_idr, &unregister_transfer_cb, xfer); return 0; } EXPORT_SYMBOL(xloop_register_transfer); EXPORT_SYMBOL(xloop_unregister_transfer); -static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx, +static blk_status_t xloop_queue_rq(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd) { struct request *rq = bd->rq; - struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq); - struct loop_device *lo = rq->q->queuedata; + struct xloop_cmd *cmd = blk_mq_rq_to_pdu(rq); + struct xloop_device *xlo = rq->q->queuedata; blk_mq_start_request(rq); - if (lo->lo_state != Lo_bound) + if (xlo->xlo_state != Xlo_bound) return BLK_STS_IOERR; switch (req_op(rq)) { @@ -1682,7 +1757,7 @@ static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx, cmd->use_aio = false; break; default: - cmd->use_aio = lo->use_dio; + cmd->use_aio = xlo->use_dio; break; } @@ -1694,103 +1769,115 @@ static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx, } else #endif cmd->css = NULL; - kthread_queue_work(&lo->worker, &cmd->work); + kthread_queue_work(&xlo->worker, &cmd->work); return BLK_STS_OK; } -static void loop_handle_cmd(struct loop_cmd *cmd) +static void xloop_handle_cmd(struct xloop_cmd *cmd) { struct request *rq = blk_mq_rq_from_pdu(cmd); const bool write = op_is_write(req_op(rq)); - struct loop_device *lo = rq->q->queuedata; + struct xloop_device *xlo = rq->q->queuedata; int ret = 0; - if (write && (lo->lo_flags & LO_FLAGS_READ_ONLY)) { + if (write && (xlo->xlo_flags & XLO_FLAGS_READ_ONLY)) { ret = -EIO; goto failed; } - ret = do_req_filebacked(lo, rq); + ret = do_req_filebacked(xlo, rq); failed: /* complete non-aio request */ if (!cmd->use_aio || ret) { - cmd->ret = ret ? -EIO : 0; + if (ret == -EOPNOTSUPP) + cmd->ret = ret; + else + cmd->ret = ret ? -EIO : 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + if (likely(!blk_should_fake_timeout(rq->q))) + blk_mq_complete_request(rq); +#else blk_mq_complete_request(rq); +#endif } } -static void loop_queue_work(struct kthread_work *work) +static void xloop_queue_work(struct kthread_work *work) { - struct loop_cmd *cmd = - container_of(work, struct loop_cmd, work); + struct xloop_cmd *cmd = + container_of(work, struct xloop_cmd, work); - loop_handle_cmd(cmd); + xloop_handle_cmd(cmd); } -static int loop_init_request(struct blk_mq_tag_set *set, struct request *rq, +static int xloop_init_request(struct blk_mq_tag_set *set, struct request *rq, unsigned int hctx_idx, unsigned int numa_node) { - struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq); + struct xloop_cmd *cmd = blk_mq_rq_to_pdu(rq); - kthread_init_work(&cmd->work, loop_queue_work); + kthread_init_work(&cmd->work, xloop_queue_work); return 0; } -static const struct blk_mq_ops loop_mq_ops = { - .queue_rq = loop_queue_rq, - .init_request = loop_init_request, - .complete = lo_complete_rq, +static const struct blk_mq_ops xloop_mq_ops = { + .queue_rq = xloop_queue_rq, + .init_request = xloop_init_request, + .complete = xlo_complete_rq, }; -static struct dentry *loop_dbgfs_dir; +static struct dentry *xloop_dbgfs_dir; -static int loop_add(struct loop_device **l, int i) +static int xloop_add(struct xloop_device **l, int i) { - struct loop_device *lo; + struct xloop_device *xlo; struct gendisk *disk; int err; err = -ENOMEM; - lo = kzalloc(sizeof(*lo), GFP_KERNEL); - if (!lo) + xlo = kzalloc(sizeof(*xlo), GFP_KERNEL); + if (!xlo) goto out; - lo->lo_state = Lo_unbound; + xlo->xlo_state = Xlo_unbound; /* allocate id, if @id >= 0, we're requesting that specific id */ if (i >= 0) { - err = idr_alloc(&loop_index_idr, lo, i, i + 1, GFP_KERNEL); + err = idr_alloc(&xloop_index_idr, xlo, i, i + 1, GFP_KERNEL); if (err == -ENOSPC) err = -EEXIST; } else { - err = idr_alloc(&loop_index_idr, lo, 0, 0, GFP_KERNEL); + err = idr_alloc(&xloop_index_idr, xlo, 0, 0, GFP_KERNEL); } if (err < 0) goto out_free_dev; i = err; err = -ENOMEM; - lo->tag_set.ops = &loop_mq_ops; - lo->tag_set.nr_hw_queues = 1; - lo->tag_set.queue_depth = 128; - lo->tag_set.numa_node = NUMA_NO_NODE; - lo->tag_set.cmd_size = sizeof(struct loop_cmd); - lo->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; - lo->tag_set.driver_data = lo; - - err = blk_mq_alloc_tag_set(&lo->tag_set); + xlo->tag_set.ops = &xloop_mq_ops; + xlo->tag_set.nr_hw_queues = 1; + xlo->tag_set.queue_depth = 128; + xlo->tag_set.numa_node = NUMA_NO_NODE; + xlo->tag_set.cmd_size = sizeof(struct xloop_cmd); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + xlo->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_STACKING; +#else + xlo->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; +#endif + xlo->tag_set.driver_data = xlo; + + err = blk_mq_alloc_tag_set(&xlo->tag_set); if (err) goto out_free_idr; - lo->lo_queue = blk_mq_init_queue(&lo->tag_set); - if (IS_ERR(lo->lo_queue)) { - err = PTR_ERR(lo->lo_queue); + xlo->xlo_queue = blk_mq_init_queue(&xlo->tag_set); + if (IS_ERR(xlo->xlo_queue)) { + err = PTR_ERR(xlo->xlo_queue); goto out_cleanup_tags; } - lo->lo_queue->queuedata = lo; + xlo->xlo_queue->queuedata = xlo; - blk_queue_max_hw_sectors(lo->lo_queue, BLK_DEF_MAX_SECTORS); + blk_queue_max_hw_sectors(xlo->xlo_queue, BLK_DEF_MAX_SECTORS); /* * By default, we do buffer IO, so it doesn't make sense to enable @@ -1798,17 +1885,17 @@ static int loop_add(struct loop_device **l, int i) * page. For directio mode, merge does help to dispatch bigger request * to underlayer disk. We will enable merge once directio is enabled. */ - blk_queue_flag_set(QUEUE_FLAG_NOMERGES, lo->lo_queue); + blk_queue_flag_set(QUEUE_FLAG_NOMERGES, xlo->xlo_queue); err = -ENOMEM; - lo->lo_fmt = loop_file_fmt_alloc(); - if (!lo->lo_fmt) + xlo->xlo_fmt = xloop_file_fmt_alloc(); + if (!xlo->xlo_fmt) goto out_free_queue; - loop_file_fmt_set_lo(lo->lo_fmt, lo); + xloop_file_fmt_set_xlo(xlo->xlo_fmt, xlo); err = -ENOMEM; - disk = lo->lo_disk = alloc_disk(1 << part_shift); + disk = xlo->xlo_disk = alloc_disk(1 << part_shift); if (!disk) goto out_free_file_fmt; @@ -1817,202 +1904,202 @@ static int loop_add(struct loop_device **l, int i) * scanning can be requested individually per-device during its * setup. Userspace can always add and remove partitions from all * devices. The needed partition minors are allocated from the - * extended minor space, the main loop device numbers will continue - * to match the loop minors, regardless of the number of partitions + * extended minor space, the main xloop device numbers will continue + * to match the xloop minors, regardless of the number of partitions * used. * * If max_part is given, partition scanning is globally enabled for - * all loop devices. The minors for the main loop devices will be + * all xloop devices. The minors for the main xloop devices will be * multiples of max_part. * * Note: Global-for-all-devices, set-only-at-init, read-only module - * parameteters like 'max_loop' and 'max_part' make things needlessly + * parameteters like 'max_xloop' and 'max_part' make things needlessly * complicated, are too static, inflexible and may surprise * userspace tools. Parameters like this in general should be avoided. */ if (!part_shift) disk->flags |= GENHD_FL_NO_PART_SCAN; disk->flags |= GENHD_FL_EXT_DEVT; - atomic_set(&lo->lo_refcnt, 0); - lo->lo_number = i; - spin_lock_init(&lo->lo_lock); - disk->major = XLOOP_MAJOR; + atomic_set(&xlo->xlo_refcnt, 0); + xlo->xlo_number = i; + spin_lock_init(&xlo->xlo_lock); + disk->major = LOOP_MAJOR; disk->first_minor = i << part_shift; - disk->fops = &lo_fops; - disk->private_data = lo; - disk->queue = lo->lo_queue; + disk->fops = &xlo_fops; + disk->private_data = xlo; + disk->queue = xlo->xlo_queue; sprintf(disk->disk_name, "xloop%d", i); add_disk(disk); - *l = lo; + *l = xlo; /* initialize debugfs entries */ /* create for each loop device a debugfs directory under 'loop' if * the 'block' directory exists, otherwise create the loop directory in * the root directory */ #ifdef CONFIG_DEBUG_FS - lo->lo_dbgfs_dir = debugfs_create_dir(disk->disk_name, loop_dbgfs_dir); + xlo->xlo_dbgfs_dir = debugfs_create_dir(disk->disk_name, xloop_dbgfs_dir); - if (IS_ERR_OR_NULL(lo->lo_dbgfs_dir)) { + if (IS_ERR_OR_NULL(xlo->xlo_dbgfs_dir)) { err = -ENODEV; - lo->lo_dbgfs_dir = NULL; + xlo->xlo_dbgfs_dir = NULL; goto out_free_file_fmt; } #endif - return lo->lo_number; + return xlo->xlo_number; out_free_file_fmt: - loop_file_fmt_free(lo->lo_fmt); + xloop_file_fmt_free(xlo->xlo_fmt); out_free_queue: - blk_cleanup_queue(lo->lo_queue); + blk_cleanup_queue(xlo->xlo_queue); out_cleanup_tags: - blk_mq_free_tag_set(&lo->tag_set); + blk_mq_free_tag_set(&xlo->tag_set); out_free_idr: - idr_remove(&loop_index_idr, i); + idr_remove(&xloop_index_idr, i); out_free_dev: - kfree(lo); + kfree(xlo); out: return err; } -static void loop_remove(struct loop_device *lo) +static void xloop_remove(struct xloop_device *xlo) { - loop_file_fmt_free(lo->lo_fmt); - debugfs_remove(lo->lo_dbgfs_dir); - del_gendisk(lo->lo_disk); - blk_cleanup_queue(lo->lo_queue); - blk_mq_free_tag_set(&lo->tag_set); - put_disk(lo->lo_disk); - kfree(lo); + xloop_file_fmt_free(xlo->xlo_fmt); + debugfs_remove(xlo->xlo_dbgfs_dir); + del_gendisk(xlo->xlo_disk); + blk_cleanup_queue(xlo->xlo_queue); + blk_mq_free_tag_set(&xlo->tag_set); + put_disk(xlo->xlo_disk); + kfree(xlo); } static int find_free_cb(int id, void *ptr, void *data) { - struct loop_device *lo = ptr; - struct loop_device **l = data; + struct xloop_device *xlo = ptr; + struct xloop_device **l = data; - if (lo->lo_state == Lo_unbound) { - *l = lo; + if (xlo->xlo_state == Xlo_unbound) { + *l = xlo; return 1; } return 0; } -static int loop_lookup(struct loop_device **l, int i) +static int xloop_lookup(struct xloop_device **l, int i) { - struct loop_device *lo; + struct xloop_device *xlo; int ret = -ENODEV; if (i < 0) { int err; - err = idr_for_each(&loop_index_idr, &find_free_cb, &lo); + err = idr_for_each(&xloop_index_idr, &find_free_cb, &xlo); if (err == 1) { - *l = lo; - ret = lo->lo_number; + *l = xlo; + ret = xlo->xlo_number; } goto out; } /* lookup and return a specific i */ - lo = idr_find(&loop_index_idr, i); - if (lo) { - *l = lo; - ret = lo->lo_number; + xlo = idr_find(&xloop_index_idr, i); + if (xlo) { + *l = xlo; + ret = xlo->xlo_number; } out: return ret; } -static struct kobject *loop_probe(dev_t dev, int *part, void *data) +static struct kobject *xloop_probe(dev_t dev, int *part, void *data) { - struct loop_device *lo; + struct xloop_device *xlo; struct kobject *kobj; int err; - mutex_lock(&loop_ctl_mutex); - err = loop_lookup(&lo, MINOR(dev) >> part_shift); + mutex_lock(&xloop_ctl_mutex); + err = xloop_lookup(&xlo, MINOR(dev) >> part_shift); if (err < 0) - err = loop_add(&lo, MINOR(dev) >> part_shift); + err = xloop_add(&xlo, MINOR(dev) >> part_shift); if (err < 0) kobj = NULL; else - kobj = get_disk_and_module(lo->lo_disk); - mutex_unlock(&loop_ctl_mutex); + kobj = get_disk_and_module(xlo->xlo_disk); + mutex_unlock(&xloop_ctl_mutex); *part = 0; return kobj; } -static long loop_control_ioctl(struct file *file, unsigned int cmd, +static long xloop_control_ioctl(struct file *file, unsigned int cmd, unsigned long parm) { - struct loop_device *lo; + struct xloop_device *xlo; int ret; - ret = mutex_lock_killable(&loop_ctl_mutex); + ret = mutex_lock_killable(&xloop_ctl_mutex); if (ret) return ret; ret = -ENOSYS; switch (cmd) { - case LOOP_CTL_ADD: - ret = loop_lookup(&lo, parm); + case XLOOP_CTL_ADD: + ret = xloop_lookup(&xlo, parm); if (ret >= 0) { ret = -EEXIST; break; } - ret = loop_add(&lo, parm); + ret = xloop_add(&xlo, parm); break; - case LOOP_CTL_REMOVE: - ret = loop_lookup(&lo, parm); + case XLOOP_CTL_REMOVE: + ret = xloop_lookup(&xlo, parm); if (ret < 0) break; - if (lo->lo_state != Lo_unbound) { + if (xlo->xlo_state != Xlo_unbound) { ret = -EBUSY; break; } - if (atomic_read(&lo->lo_refcnt) > 0) { + if (atomic_read(&xlo->xlo_refcnt) > 0) { ret = -EBUSY; break; } - lo->lo_disk->private_data = NULL; - idr_remove(&loop_index_idr, lo->lo_number); - loop_remove(lo); + xlo->xlo_disk->private_data = NULL; + idr_remove(&xloop_index_idr, xlo->xlo_number); + xloop_remove(xlo); break; - case LOOP_CTL_GET_FREE: - ret = loop_lookup(&lo, -1); + case XLOOP_CTL_GET_FREE: + ret = xloop_lookup(&xlo, -1); if (ret >= 0) break; - ret = loop_add(&lo, -1); + ret = xloop_add(&xlo, -1); } - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&xloop_ctl_mutex); return ret; } -static const struct file_operations loop_ctl_fops = { +static const struct file_operations xloop_ctl_fops = { .open = nonseekable_open, - .unlocked_ioctl = loop_control_ioctl, - .compat_ioctl = loop_control_ioctl, + .unlocked_ioctl = xloop_control_ioctl, + .compat_ioctl = xloop_control_ioctl, .owner = THIS_MODULE, .llseek = noop_llseek, }; -static struct miscdevice loop_misc = { - .minor = XLOOP_CTRL_MINOR, +static struct miscdevice xloop_misc = { + .minor = LOOP_CTRL_MINOR, .name = "xloop-control", - .fops = &loop_ctl_fops, + .fops = &xloop_ctl_fops, }; -MODULE_ALIAS_MISCDEV(XLOOP_CTRL_MINOR); +MODULE_ALIAS_MISCDEV(LOOP_CTRL_MINOR); MODULE_ALIAS("devname:xloop-control"); static int __init xloop_init(void) { int i, nr; unsigned long range; - struct loop_device *lo; + struct xloop_device *xlo; int err; part_shift = 0; @@ -2035,68 +2122,65 @@ static int __init xloop_init(void) goto err_out; } - if (max_loop > 1UL << (MINORBITS - part_shift)) { + if (max_xloop > 1UL << (MINORBITS - part_shift)) { err = -EINVAL; goto err_out; } /* - * If max_loop is specified, create that many devices upfront. - * This also becomes a hard limit. If max_loop is not specified, - * create CONFIG_BLK_DEV_LOOP_MIN_COUNT loop devices at module - * init time. Loop devices can be requested on-demand with the + * If max_xloop is specified, create that many devices upfront. + * This also becomes a hard limit. If max_xloop is not specified, + * create CONFIG_BLK_DEV_XLOOP_MIN_COUNT xloop devices at module + * init time. xloop devices can be requested on-demand with the * /dev/xloop-control interface, or be instantiated by accessing * a 'dead' device node. */ - if (max_loop) { - nr = max_loop; - range = max_loop << part_shift; - } else { + if (max_xloop) { nr = CONFIG_BLK_DEV_LOOP_MIN_COUNT; range = 1UL << MINORBITS; } - err = misc_register(&loop_misc); + err = misc_register(&xloop_misc); if (err < 0) goto err_out; - if (register_blkdev(XLOOP_MAJOR, "xloop")) { + if (register_blkdev(LOOP_MAJOR, "xloop")) { err = -EIO; goto misc_out; } #ifdef CONFIG_DEBUG_FS - loop_dbgfs_dir = debugfs_create_dir("xloop", NULL); - if (IS_ERR_OR_NULL(loop_dbgfs_dir)) { + xloop_dbgfs_dir = debugfs_create_dir("xloop", NULL); + if (IS_ERR_OR_NULL(xloop_dbgfs_dir)) { err = -ENODEV; goto misc_out; } #endif - blk_register_region(MKDEV(XLOOP_MAJOR, 0), range, - THIS_MODULE, loop_probe, NULL, NULL); + blk_register_region(MKDEV(LOOP_MAJOR, 0), range, + THIS_MODULE, xloop_probe, NULL, NULL); - /* pre-create number of devices given by config or max_loop */ - mutex_lock(&loop_ctl_mutex); + /* pre-create number of devices given by config or max_xloop */ + mutex_lock(&xloop_ctl_mutex); for (i = 0; i < nr; i++) - loop_add(&lo, i); - mutex_unlock(&loop_ctl_mutex); + xloop_add(&xlo, i); + mutex_unlock(&xloop_ctl_mutex); - printk(KERN_INFO "loop: module loaded\n"); + printk(KERN_INFO "xloop: module loaded\n"); return 0; misc_out: - misc_deregister(&loop_misc); + misc_deregister(&xloop_misc); err_out: return err; } -static int loop_exit_cb(int id, void *ptr, void *data) +static int xloop_exit_cb(int id, void *ptr, void *data) { - struct loop_device *lo = ptr; + struct xloop_device *xlo = ptr; - loop_remove(lo); + xloop_remove(xlo); return 0; } @@ -2104,30 +2188,34 @@ static void __exit xloop_exit(void) { unsigned long range; - range = max_loop ? max_loop << part_shift : 1UL << MINORBITS; + range = max_xloop ? max_xloop << part_shift : 1UL << MINORBITS; + + mutex_lock(&xloop_ctl_mutex); - idr_for_each(&loop_index_idr, &loop_exit_cb, NULL); - idr_destroy(&loop_index_idr); + idr_for_each(&xloop_index_idr, &xloop_exit_cb, NULL); + idr_destroy(&xloop_index_idr); - blk_unregister_region(MKDEV(XLOOP_MAJOR, 0), range); - unregister_blkdev(XLOOP_MAJOR, "xloop"); + blk_unregister_region(MKDEV(LOOP_MAJOR, 0), range); + unregister_blkdev(LOOP_MAJOR, "xloop"); #ifdef CONFIG_DEBUG_FS - debugfs_remove(loop_dbgfs_dir); + debugfs_remove(xloop_dbgfs_dir); #endif - misc_deregister(&loop_misc); + misc_deregister(&xloop_misc); + + mutex_unlock(&xloop_ctl_mutex); } module_init(xloop_init); module_exit(xloop_exit); #ifndef MODULE -static int __init max_loop_setup(char *str) +static int __init max_xloop_setup(char *str) { - max_loop = simple_strtol(str, NULL, 0); + max_xloop = simple_strtol(str, NULL, 0); return 1; } -__setup("max_loop=", max_loop_setup); +__setup("max_xloop=", max_xloop_setup); #endif diff --git a/loop_main.h b/loop_main.h index eec7ceb..1b5851a 100644 --- a/loop_main.h +++ b/loop_main.h @@ -6,8 +6,8 @@ * Copyright 1993 by Theodore Ts'o. Redistribution of this file is * permitted under the GNU General Public License. */ -#ifndef _LINUX_LOOP_H -#define _LINUX_LOOP_H +#ifndef _LINUX_XLOOP_H +#define _LINUX_XLOOP_H #include <linux/bio.h> #include <linux/blkdev.h> @@ -15,105 +15,67 @@ #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/kthread.h> -#include <uapi/linux/loop.h> - +#include "uapi/linux/loop.h" #ifdef CONFIG_DEBUG_FS #include <linux/debugfs.h> #endif #include "loop_file_fmt.h" -// See: https://www.kernel.org/doc/Documentation/admin-guide/devices.txt -#define XLOOP_MAJOR 120 -#define XLOOP_CTRL_MINOR 142 - /* Possible states of device */ enum { - Lo_unbound, - Lo_bound, - Lo_rundown, -}; - -struct loop_func_table; - -struct xloop_info { - int lo_number; /* ioctl r/o */ - __kernel_old_dev_t lo_device; /* ioctl r/o */ - unsigned long lo_inode; /* ioctl r/o */ - __kernel_old_dev_t lo_rdevice; /* ioctl r/o */ - int lo_offset; - int lo_encrypt_type; - int lo_encrypt_key_size; /* ioctl w/o */ - int lo_flags; /* ioctl r/o */ - char lo_name[LO_NAME_SIZE]; - unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ - unsigned long lo_init[2]; - char reserved[4]; - int lo_file_fmt_type; + Xlo_unbound, + Xlo_bound, + Xlo_rundown, }; -struct xloop_info64 { - __u64 lo_device; /* ioctl r/o */ - __u64 lo_inode; /* ioctl r/o */ - __u64 lo_rdevice; /* ioctl r/o */ - __u64 lo_offset; - __u64 lo_sizelimit;/* bytes, 0 == max available */ - __u32 lo_number; /* ioctl r/o */ - __u32 lo_encrypt_type; - __u32 lo_encrypt_key_size; /* ioctl w/o */ - __u32 lo_flags; /* ioctl r/o */ - __u8 lo_file_name[LO_NAME_SIZE]; - __u8 lo_crypt_name[LO_NAME_SIZE]; - __u8 lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ - __u64 lo_init[2]; - __u32 lo_file_fmt_type; -} __attribute__((packed)); +struct xloop_func_table; -struct loop_device { - int lo_number; - atomic_t lo_refcnt; - loff_t lo_offset; - loff_t lo_sizelimit; - int lo_flags; - int (*transfer)(struct loop_device *, int cmd, +struct xloop_device { + int xlo_number; + atomic_t xlo_refcnt; + loff_t xlo_offset; + loff_t xlo_sizelimit; + int xlo_flags; + int (*transfer)(struct xloop_device *, int cmd, struct page *raw_page, unsigned raw_off, - struct page *loop_page, unsigned loop_off, + struct page *xloop_page, unsigned xloop_off, int size, sector_t real_block); - char lo_file_name[LO_NAME_SIZE]; - char lo_crypt_name[LO_NAME_SIZE]; - char lo_encrypt_key[LO_KEY_SIZE]; - int lo_encrypt_key_size; - struct loop_func_table *lo_encryption; - __u32 lo_init[2]; - kuid_t lo_key_owner; /* Who set the key */ - int (*ioctl)(struct loop_device *, int cmd, + char xlo_file_name[XLO_NAME_SIZE]; + char xlo_crypt_name[XLO_NAME_SIZE]; + char xlo_encrypt_key[XLO_KEY_SIZE]; + int xlo_encrypt_key_size; + struct xloop_func_table *xlo_encryption; + __u32 xlo_init[2]; + kuid_t xlo_key_owner; /* Who set the key */ + int (*ioctl)(struct xloop_device *, int cmd, unsigned long arg); - struct loop_file_fmt *lo_fmt; + struct xloop_file_fmt *xlo_fmt; - struct file * lo_backing_file; - struct block_device *lo_device; + struct file * xlo_backing_file; + struct block_device *xlo_device; void *key_data; gfp_t old_gfp_mask; - spinlock_t lo_lock; - int lo_state; + spinlock_t xlo_lock; + int xlo_state; struct kthread_worker worker; struct task_struct *worker_task; bool use_dio; bool sysfs_inited; - struct request_queue *lo_queue; + struct request_queue *xlo_queue; struct blk_mq_tag_set tag_set; - struct gendisk *lo_disk; + struct gendisk *xlo_disk; #ifdef CONFIG_DEBUG_FS - struct dentry *lo_dbgfs_dir; + struct dentry *xlo_dbgfs_dir; #endif }; -struct loop_cmd { +struct xloop_cmd { struct kthread_work work; bool use_aio; /* use AIO interface to handle I/O */ atomic_t ref; /* only for aio */ @@ -124,20 +86,20 @@ struct loop_cmd { }; /* Support for loadable transfer modules */ -struct loop_func_table { +struct xloop_func_table { int number; /* filter type */ - int (*transfer)(struct loop_device *lo, int cmd, + int (*transfer)(struct xloop_device *xlo, int cmd, struct page *raw_page, unsigned raw_off, - struct page *loop_page, unsigned loop_off, + struct page *xloop_page, unsigned xloop_off, int size, sector_t real_block); - int (*init)(struct loop_device *, const struct xloop_info64 *); - /* release is called from loop_unregister_transfer or clr_fd */ - int (*release)(struct loop_device *); - int (*ioctl)(struct loop_device *, int cmd, unsigned long arg); + int (*init)(struct xloop_device *, const struct xloop_info64 *); + /* release is called from xloop_unregister_transfer or clr_fd */ + int (*release)(struct xloop_device *); + int (*ioctl)(struct xloop_device *, int cmd, unsigned long arg); struct module *owner; }; -int xloop_register_transfer(struct loop_func_table *funcs); -int xloop_unregister_transfer(int number); +int xloop_register_transfer(struct xloop_func_table *funcs); +int xloop_unregister_transfer(int number); #endif diff --git a/uapi/linux/loop.h b/uapi/linux/loop.h new file mode 100644 index 0000000..f93f6ad --- /dev/null +++ b/uapi/linux/loop.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-1.0+ WITH Linux-syscall-note */ +/* + * include/linux/loop.h + * + * Written by Theodore Ts'o, 3/29/93. + * + * Copyright 1993 by Theodore Ts'o. Redistribution of this file is + * permitted under the GNU General Public License. + */ +#ifndef _UAPI_LINUX_XLOOP_H +#define _UAPI_LINUX_XLOOP_H + + +#define XLO_NAME_SIZE 64 +#define XLO_KEY_SIZE 32 + + +/* + * xloop flags + */ +enum { + XLO_FLAGS_READ_ONLY = 1, + XLO_FLAGS_AUTOCLEAR = 4, + XLO_FLAGS_PARTSCAN = 8, + XLO_FLAGS_DIRECT_IO = 16, +}; + +/* XLO_FLAGS that can be set using XLOOP_SET_STATUS(64) */ +#define XLOOP_SET_STATUS_SETTABLE_FLAGS (XLO_FLAGS_AUTOCLEAR | XLO_FLAGS_PARTSCAN) + +/* XLO_FLAGS that can be cleared using XLOOP_SET_STATUS(64) */ +#define XLOOP_SET_STATUS_CLEARABLE_FLAGS (XLO_FLAGS_AUTOCLEAR) + +/* XLO_FLAGS that can be set using XLOOP_CONFIGURE */ +#define XLOOP_CONFIGURE_SETTABLE_FLAGS (XLO_FLAGS_READ_ONLY | XLO_FLAGS_AUTOCLEAR \ + | XLO_FLAGS_PARTSCAN | XLO_FLAGS_DIRECT_IO) + +#include <asm/posix_types.h> /* for __kernel_old_dev_t */ +#include <linux/types.h> /* for __u64 */ + +/* Backwards compatibility version */ +struct xloop_info { + int xlo_number; /* ioctl r/o */ + __kernel_old_dev_t xlo_device; /* ioctl r/o */ + unsigned long xlo_inode; /* ioctl r/o */ + __kernel_old_dev_t xlo_rdevice; /* ioctl r/o */ + int xlo_offset; + int xlo_encrypt_type; + int xlo_encrypt_key_size; /* ioctl w/o */ + int xlo_flags; + char xlo_name[XLO_NAME_SIZE]; + unsigned char xlo_encrypt_key[XLO_KEY_SIZE]; /* ioctl w/o */ + unsigned long xlo_init[2]; + char reserved[4]; + int xlo_file_fmt_type; +}; + +struct xloop_info64 { + __u64 xlo_device; /* ioctl r/o */ + __u64 xlo_inode; /* ioctl r/o */ + __u64 xlo_rdevice; /* ioctl r/o */ + __u64 xlo_offset; + __u64 xlo_sizelimit; /* bytes, 0 == max available */ + __u32 xlo_number; /* ioctl r/o */ + __u32 xlo_encrypt_type; + __u32 xlo_encrypt_key_size; /* ioctl w/o */ + __u32 xlo_flags; + __u8 xlo_file_name[XLO_NAME_SIZE]; + __u8 xlo_crypt_name[XLO_NAME_SIZE]; + __u8 xlo_encrypt_key[XLO_KEY_SIZE]; /* ioctl w/o */ + __u64 xlo_init[2]; + __u32 xlo_file_fmt_type; +}; + +/** + * struct xloop_config - Complete configuration for a xloop device. + * @fd: fd of the file to be used as a backing file for the xloop device. + * @block_size: block size to use; ignored if 0. + * @info: struct xloop_info64 to configure the xloop device with. + * + * This structure is used with the XLOOP_CONFIGURE ioctl, and can be used to + * atomically setup and configure all xloop device parameters at once. + */ +struct xloop_config { + __u32 fd; + __u32 block_size; + struct xloop_info64 info; + __u64 __reserved[8]; +}; + +/* + * xloop filter types + */ +#define XLO_CRYPT_NONE 0 +#define XLO_CRYPT_XOR 1 +#define XLO_CRYPT_DES 2 +#define XLO_CRYPT_FISH2 3 /* Twofish encryption */ +#define XLO_CRYPT_BLOW 4 +#define XLO_CRYPT_CAST128 5 +#define XLO_CRYPT_IDEA 6 +#define XLO_CRYPT_DUMMY 9 +#define XLO_CRYPT_SKIPJACK 10 +#define XLO_CRYPT_CRYPTOAPI 18 +#define MAX_XLO_CRYPT 20 + +/* + * IOCTL commands --- we will commandeer 0x4C ('L') + */ +#define XLOOP_SET_FD 0x4C00 +#define XLOOP_CLR_FD 0x4C01 +#define XLOOP_SET_STATUS 0x4C02 +#define XLOOP_GET_STATUS 0x4C03 +#define XLOOP_SET_STATUS64 0x4C04 +#define XLOOP_GET_STATUS64 0x4C05 +#define XLOOP_CHANGE_FD 0x4C06 +#define XLOOP_SET_CAPACITY 0x4C07 +#define XLOOP_SET_DIRECT_IO 0x4C08 +#define XLOOP_SET_BLOCK_SIZE 0x4C09 +#define XLOOP_CONFIGURE 0x4C0A + +/* /dev/xloop-control interface */ +#define XLOOP_CTL_ADD 0x4C80 +#define XLOOP_CTL_REMOVE 0x4C81 +#define XLOOP_CTL_GET_FREE 0x4C82 +#endif /* _UAPI_LINUX_XLOOP_H */ |