2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2001-2003 Sistina Software (UK) Limited.
|
|
|
|
*
|
|
|
|
* This file is released under the GPL.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "dm.h"
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/blkdev.h>
|
|
|
|
#include <linux/bio.h>
|
2017-04-13 04:37:44 +08:00
|
|
|
#include <linux/dax.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/slab.h>
|
2008-10-22 00:44:59 +08:00
|
|
|
#include <linux/device-mapper.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-06-26 15:27:35 +08:00
|
|
|
#define DM_MSG_PREFIX "linear"
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Linear: maps a linear range of a device.
|
|
|
|
*/
|
|
|
|
struct linear_c {
|
|
|
|
struct dm_dev *dev;
|
|
|
|
sector_t start;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct a linear mapping: <dev_path> <offset>
|
|
|
|
*/
|
|
|
|
static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct linear_c *lc;
|
2006-03-27 17:17:48 +08:00
|
|
|
unsigned long long tmp;
|
dm: reject trailing characters in sccanf input
Device mapper uses sscanf to convert arguments to numbers. The problem is that
the way we use it ignores additional unmatched characters in the scanned string.
For example, this `if (sscanf(string, "%d", &number) == 1)' will match a number,
but also it will match number with some garbage appended, like "123abc".
As a result, device mapper accepts garbage after some numbers. For example
the command `dmsetup create vg1-new --table "0 16384 linear 254:1bla 34816bla"'
will pass without an error.
This patch fixes all sscanf uses in device mapper. It appends "%c" with
a pointer to a dummy character variable to every sscanf statement.
The construct `if (sscanf(string, "%d%c", &number, &dummy) == 1)' succeeds
only if string is a null-terminated number (optionally preceded by some
whitespace characters). If there is some character appended after the number,
sscanf matches "%c", writes the character to the dummy variable and returns 2.
We check the return value for 1 and consequently reject numbers with some
garbage appended.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Acked-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
2012-03-29 01:41:26 +08:00
|
|
|
char dummy;
|
2015-07-31 21:20:36 +08:00
|
|
|
int ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (argc != 2) {
|
2006-06-26 15:27:35 +08:00
|
|
|
ti->error = "Invalid argument count";
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
lc = kmalloc(sizeof(*lc), GFP_KERNEL);
|
|
|
|
if (lc == NULL) {
|
2015-10-28 03:38:58 +08:00
|
|
|
ti->error = "Cannot allocate linear context";
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-07-31 21:20:36 +08:00
|
|
|
ret = -EINVAL;
|
dm: reject trailing characters in sccanf input
Device mapper uses sscanf to convert arguments to numbers. The problem is that
the way we use it ignores additional unmatched characters in the scanned string.
For example, this `if (sscanf(string, "%d", &number) == 1)' will match a number,
but also it will match number with some garbage appended, like "123abc".
As a result, device mapper accepts garbage after some numbers. For example
the command `dmsetup create vg1-new --table "0 16384 linear 254:1bla 34816bla"'
will pass without an error.
This patch fixes all sscanf uses in device mapper. It appends "%c" with
a pointer to a dummy character variable to every sscanf statement.
The construct `if (sscanf(string, "%d%c", &number, &dummy) == 1)' succeeds
only if string is a null-terminated number (optionally preceded by some
whitespace characters). If there is some character appended after the number,
sscanf matches "%c", writes the character to the dummy variable and returns 2.
We check the return value for 1 and consequently reject numbers with some
garbage appended.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Acked-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
2012-03-29 01:41:26 +08:00
|
|
|
if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1) {
|
2015-10-28 03:38:58 +08:00
|
|
|
ti->error = "Invalid device sector";
|
2005-04-17 06:20:36 +08:00
|
|
|
goto bad;
|
|
|
|
}
|
2006-03-27 17:17:48 +08:00
|
|
|
lc->start = tmp;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-07-31 21:20:36 +08:00
|
|
|
ret = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &lc->dev);
|
|
|
|
if (ret) {
|
2015-10-28 03:38:58 +08:00
|
|
|
ti->error = "Device lookup failed";
|
2005-04-17 06:20:36 +08:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2013-03-02 06:45:47 +08:00
|
|
|
ti->num_flush_bios = 1;
|
|
|
|
ti->num_discard_bios = 1;
|
2018-03-13 17:23:45 +08:00
|
|
|
ti->num_secure_erase_bios = 1;
|
2013-03-02 06:45:47 +08:00
|
|
|
ti->num_write_same_bios = 1;
|
2017-04-06 01:21:05 +08:00
|
|
|
ti->num_write_zeroes_bios = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
ti->private = lc;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
bad:
|
|
|
|
kfree(lc);
|
2015-07-31 21:20:36 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void linear_dtr(struct dm_target *ti)
|
|
|
|
{
|
|
|
|
struct linear_c *lc = (struct linear_c *) ti->private;
|
|
|
|
|
|
|
|
dm_put_device(ti, lc->dev);
|
|
|
|
kfree(lc);
|
|
|
|
}
|
|
|
|
|
2008-07-21 19:00:38 +08:00
|
|
|
static sector_t linear_map_sector(struct dm_target *ti, sector_t bi_sector)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-21 19:00:38 +08:00
|
|
|
struct linear_c *lc = ti->private;
|
|
|
|
|
2010-08-12 11:14:11 +08:00
|
|
|
return lc->start + dm_target_offset(ti, bi_sector);
|
2008-07-21 19:00:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void linear_map_bio(struct dm_target *ti, struct bio *bio)
|
|
|
|
{
|
|
|
|
struct linear_c *lc = ti->private;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-08-24 01:10:32 +08:00
|
|
|
bio_set_dev(bio, lc->dev->bdev);
|
2017-05-09 07:40:50 +08:00
|
|
|
if (bio_sectors(bio) || bio_op(bio) == REQ_OP_ZONE_RESET)
|
2013-10-12 06:44:27 +08:00
|
|
|
bio->bi_iter.bi_sector =
|
|
|
|
linear_map_sector(ti, bio->bi_iter.bi_sector);
|
2008-07-21 19:00:38 +08:00
|
|
|
}
|
|
|
|
|
2012-12-22 04:23:41 +08:00
|
|
|
static int linear_map(struct dm_target *ti, struct bio *bio)
|
2008-07-21 19:00:38 +08:00
|
|
|
{
|
|
|
|
linear_map_bio(ti, bio);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-08 18:41:06 +08:00
|
|
|
return DM_MAPIO_REMAPPED;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-03-02 06:45:44 +08:00
|
|
|
static void linear_status(struct dm_target *ti, status_type_t type,
|
|
|
|
unsigned status_flags, char *result, unsigned maxlen)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct linear_c *lc = (struct linear_c *) ti->private;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case STATUSTYPE_INFO:
|
|
|
|
result[0] = '\0';
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATUSTYPE_TABLE:
|
2006-03-27 17:17:48 +08:00
|
|
|
snprintf(result, maxlen, "%s %llu", lc->dev->name,
|
|
|
|
(unsigned long long)lc->start);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-04 04:54:10 +08:00
|
|
|
static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
|
2006-10-03 16:15:18 +08:00
|
|
|
{
|
|
|
|
struct linear_c *lc = (struct linear_c *) ti->private;
|
2012-01-12 23:01:29 +08:00
|
|
|
struct dm_dev *dev = lc->dev;
|
2015-10-15 20:10:50 +08:00
|
|
|
|
|
|
|
*bdev = dev->bdev;
|
2012-01-12 23:01:29 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Only pass ioctls through if the device sizes match exactly.
|
|
|
|
*/
|
|
|
|
if (lc->start ||
|
|
|
|
ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT)
|
2015-10-15 20:10:50 +08:00
|
|
|
return 1;
|
|
|
|
return 0;
|
2006-10-03 16:15:18 +08:00
|
|
|
}
|
|
|
|
|
2018-10-12 18:08:49 +08:00
|
|
|
#ifdef CONFIG_BLK_DEV_ZONED
|
|
|
|
static int linear_report_zones(struct dm_target *ti, sector_t sector,
|
|
|
|
struct blk_zone *zones, unsigned int *nr_zones,
|
|
|
|
gfp_t gfp_mask)
|
|
|
|
{
|
|
|
|
struct linear_c *lc = (struct linear_c *) ti->private;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Do report and remap it */
|
|
|
|
ret = blkdev_report_zones(lc->dev->bdev, linear_map_sector(ti, sector),
|
|
|
|
zones, nr_zones, gfp_mask);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (*nr_zones)
|
|
|
|
dm_remap_zone_report(ti, lc->start, zones, nr_zones);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-06-22 17:12:33 +08:00
|
|
|
static int linear_iterate_devices(struct dm_target *ti,
|
|
|
|
iterate_devices_callout_fn fn, void *data)
|
|
|
|
{
|
|
|
|
struct linear_c *lc = ti->private;
|
|
|
|
|
2009-07-24 03:30:42 +08:00
|
|
|
return fn(ti, lc->dev, lc->start, ti->len, data);
|
2009-06-22 17:12:33 +08:00
|
|
|
}
|
|
|
|
|
2018-03-30 08:22:13 +08:00
|
|
|
#if IS_ENABLED(CONFIG_DAX_DRIVER)
|
2017-04-13 04:37:44 +08:00
|
|
|
static long linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff,
|
|
|
|
long nr_pages, void **kaddr, pfn_t *pfn)
|
2016-06-23 07:54:54 +08:00
|
|
|
{
|
2017-04-13 04:37:44 +08:00
|
|
|
long ret;
|
2016-06-23 07:54:54 +08:00
|
|
|
struct linear_c *lc = ti->private;
|
|
|
|
struct block_device *bdev = lc->dev->bdev;
|
2017-04-13 04:37:44 +08:00
|
|
|
struct dax_device *dax_dev = lc->dev->dax_dev;
|
|
|
|
sector_t dev_sector, sector = pgoff * PAGE_SECTORS;
|
|
|
|
|
|
|
|
dev_sector = linear_map_sector(ti, sector);
|
|
|
|
ret = bdev_dax_pgoff(bdev, dev_sector, nr_pages * PAGE_SIZE, &pgoff);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
return dax_direct_access(dax_dev, pgoff, nr_pages, kaddr, pfn);
|
2016-06-23 07:54:54 +08:00
|
|
|
}
|
|
|
|
|
2017-05-30 03:57:56 +08:00
|
|
|
static size_t linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff,
|
|
|
|
void *addr, size_t bytes, struct iov_iter *i)
|
|
|
|
{
|
|
|
|
struct linear_c *lc = ti->private;
|
|
|
|
struct block_device *bdev = lc->dev->bdev;
|
|
|
|
struct dax_device *dax_dev = lc->dev->dax_dev;
|
|
|
|
sector_t dev_sector, sector = pgoff * PAGE_SECTORS;
|
|
|
|
|
|
|
|
dev_sector = linear_map_sector(ti, sector);
|
|
|
|
if (bdev_dax_pgoff(bdev, dev_sector, ALIGN(bytes, PAGE_SIZE), &pgoff))
|
|
|
|
return 0;
|
|
|
|
return dax_copy_from_iter(dax_dev, pgoff, addr, bytes, i);
|
|
|
|
}
|
|
|
|
|
2018-05-02 21:46:33 +08:00
|
|
|
static size_t linear_dax_copy_to_iter(struct dm_target *ti, pgoff_t pgoff,
|
|
|
|
void *addr, size_t bytes, struct iov_iter *i)
|
|
|
|
{
|
|
|
|
struct linear_c *lc = ti->private;
|
|
|
|
struct block_device *bdev = lc->dev->bdev;
|
|
|
|
struct dax_device *dax_dev = lc->dev->dax_dev;
|
|
|
|
sector_t dev_sector, sector = pgoff * PAGE_SECTORS;
|
|
|
|
|
|
|
|
dev_sector = linear_map_sector(ti, sector);
|
|
|
|
if (bdev_dax_pgoff(bdev, dev_sector, ALIGN(bytes, PAGE_SIZE), &pgoff))
|
|
|
|
return 0;
|
|
|
|
return dax_copy_to_iter(dax_dev, pgoff, addr, bytes, i);
|
|
|
|
}
|
|
|
|
|
2018-03-30 08:22:13 +08:00
|
|
|
#else
|
|
|
|
#define linear_dax_direct_access NULL
|
|
|
|
#define linear_dax_copy_from_iter NULL
|
2018-05-02 21:46:33 +08:00
|
|
|
#define linear_dax_copy_to_iter NULL
|
2018-03-30 08:22:13 +08:00
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct target_type linear_target = {
|
|
|
|
.name = "linear",
|
2017-05-09 07:40:50 +08:00
|
|
|
.version = {1, 4, 0},
|
2018-10-11 10:45:30 +08:00
|
|
|
#ifdef CONFIG_BLK_DEV_ZONED
|
2017-05-09 07:40:50 +08:00
|
|
|
.features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_ZONED_HM,
|
2018-10-12 18:08:49 +08:00
|
|
|
.report_zones = linear_report_zones,
|
2018-10-11 00:01:55 +08:00
|
|
|
#else
|
|
|
|
.features = DM_TARGET_PASSES_INTEGRITY,
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
.module = THIS_MODULE,
|
|
|
|
.ctr = linear_ctr,
|
|
|
|
.dtr = linear_dtr,
|
|
|
|
.map = linear_map,
|
|
|
|
.status = linear_status,
|
2015-10-15 20:10:50 +08:00
|
|
|
.prepare_ioctl = linear_prepare_ioctl,
|
2009-06-22 17:12:33 +08:00
|
|
|
.iterate_devices = linear_iterate_devices,
|
2017-04-13 04:37:44 +08:00
|
|
|
.direct_access = linear_dax_direct_access,
|
2017-05-30 03:57:56 +08:00
|
|
|
.dax_copy_from_iter = linear_dax_copy_from_iter,
|
2018-05-02 21:46:33 +08:00
|
|
|
.dax_copy_to_iter = linear_dax_copy_to_iter,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
int __init dm_linear_init(void)
|
|
|
|
{
|
|
|
|
int r = dm_register_target(&linear_target);
|
|
|
|
|
|
|
|
if (r < 0)
|
2006-06-26 15:27:35 +08:00
|
|
|
DMERR("register failed %d", r);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dm_linear_exit(void)
|
|
|
|
{
|
2009-01-06 11:04:58 +08:00
|
|
|
dm_unregister_target(&linear_target);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|