diff options
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/dm-table.c | 58 |
1 files changed, 44 insertions, 14 deletions
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index af1ceae2582a..535fdaf2473d 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c | |||
@@ -383,16 +383,45 @@ static void close_dev(struct dm_dev_internal *d, struct mapped_device *md) | |||
383 | /* | 383 | /* |
384 | * If possible, this checks an area of a destination device is valid. | 384 | * If possible, this checks an area of a destination device is valid. |
385 | */ | 385 | */ |
386 | static int check_device_area(struct dm_dev_internal *dd, sector_t start, | 386 | static int device_area_is_valid(struct dm_target *ti, struct block_device *bdev, |
387 | sector_t len) | 387 | sector_t start, sector_t len) |
388 | { | 388 | { |
389 | sector_t dev_size = i_size_read(dd->dm_dev.bdev->bd_inode) >> | 389 | sector_t dev_size = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT; |
390 | SECTOR_SHIFT; | 390 | unsigned short logical_block_size_sectors = |
391 | ti->limits.logical_block_size >> SECTOR_SHIFT; | ||
392 | char b[BDEVNAME_SIZE]; | ||
391 | 393 | ||
392 | if (!dev_size) | 394 | if (!dev_size) |
393 | return 1; | 395 | return 1; |
394 | 396 | ||
395 | return ((start < dev_size) && (len <= (dev_size - start))); | 397 | if ((start >= dev_size) || (start + len > dev_size)) { |
398 | DMWARN("%s: %s too small for target", | ||
399 | dm_device_name(ti->table->md), bdevname(bdev, b)); | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | if (logical_block_size_sectors <= 1) | ||
404 | return 1; | ||
405 | |||
406 | if (start & (logical_block_size_sectors - 1)) { | ||
407 | DMWARN("%s: start=%llu not aligned to h/w " | ||
408 | "logical block size %hu of %s", | ||
409 | dm_device_name(ti->table->md), | ||
410 | (unsigned long long)start, | ||
411 | ti->limits.logical_block_size, bdevname(bdev, b)); | ||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | if (len & (logical_block_size_sectors - 1)) { | ||
416 | DMWARN("%s: len=%llu not aligned to h/w " | ||
417 | "logical block size %hu of %s", | ||
418 | dm_device_name(ti->table->md), | ||
419 | (unsigned long long)len, | ||
420 | ti->limits.logical_block_size, bdevname(bdev, b)); | ||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | return 1; | ||
396 | } | 425 | } |
397 | 426 | ||
398 | /* | 427 | /* |
@@ -478,14 +507,7 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti, | |||
478 | } | 507 | } |
479 | atomic_inc(&dd->count); | 508 | atomic_inc(&dd->count); |
480 | 509 | ||
481 | if (!check_device_area(dd, start, len)) { | ||
482 | DMWARN("device %s too small for target", path); | ||
483 | dm_put_device(ti, &dd->dm_dev); | ||
484 | return -EINVAL; | ||
485 | } | ||
486 | |||
487 | *result = &dd->dm_dev; | 510 | *result = &dd->dm_dev; |
488 | |||
489 | return 0; | 511 | return 0; |
490 | } | 512 | } |
491 | 513 | ||
@@ -554,8 +576,16 @@ int dm_get_device(struct dm_target *ti, const char *path, sector_t start, | |||
554 | int r = __table_get_device(ti->table, ti, path, | 576 | int r = __table_get_device(ti->table, ti, path, |
555 | start, len, mode, result); | 577 | start, len, mode, result); |
556 | 578 | ||
557 | if (!r) | 579 | if (r) |
558 | dm_set_device_limits(ti, (*result)->bdev); | 580 | return r; |
581 | |||
582 | dm_set_device_limits(ti, (*result)->bdev); | ||
583 | |||
584 | if (!device_area_is_valid(ti, (*result)->bdev, start, len)) { | ||
585 | dm_put_device(ti, *result); | ||
586 | *result = NULL; | ||
587 | return -EINVAL; | ||
588 | } | ||
559 | 589 | ||
560 | return r; | 590 | return r; |
561 | } | 591 | } |