summaryrefslogtreecommitdiffstats
path: root/loop_file_fmt_raw.c
diff options
context:
space:
mode:
authorManuel Bentele2020-09-07 14:16:19 +0200
committerManuel Bentele2020-09-16 07:34:59 +0200
commit20beabd7fc25cab198fac90a79de6cf3e7339301 (patch)
treec8f4dee5d209fc09ea3135d48a14e66164313317 /loop_file_fmt_raw.c
parentCache last decompressed cluster for compressed QCOW (diff)
downloadxloop-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.
Diffstat (limited to 'loop_file_fmt_raw.c')
-rw-r--r--loop_file_fmt_raw.c252
1 files changed, 134 insertions, 118 deletions
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");