| From ae0b7448e91353ea5f821601a055aca6b58042cd Mon Sep 17 00:00:00 2001 |
| From: Mikulas Patocka <mpatocka@redhat.com> |
| Date: Fri, 4 Sep 2009 20:40:43 +0100 |
| Subject: dm snapshot: fix on disk chunk size validation |
| |
| From: Mikulas Patocka <mpatocka@redhat.com> |
| |
| commit ae0b7448e91353ea5f821601a055aca6b58042cd upstream. |
| |
| Fix some problems seen in the chunk size processing when activating a |
| pre-existing snapshot. |
| |
| For a new snapshot, the chunk size can either be supplied by the creator |
| or a default value can be used. For an existing snapshot, the |
| chunk size in the snapshot header on disk should always be used. |
| |
| If someone attempts to load an existing snapshot and has the 'default |
| chunk size' option set, the kernel uses its default value even when it |
| is incorrect for the snapshot being loaded. This patch ensures the |
| correct on-disk value is always used. |
| |
| Secondly, when the code does use the chunk size stored on the disk it is |
| prudent to revalidate it, so the code can exit cleanly if it got |
| corrupted as happened in |
| https://bugzilla.redhat.com/show_bug.cgi?id=461506 . |
| |
| Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> |
| Signed-off-by: Alasdair G Kergon <agk@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| drivers/md/dm-exception-store.c | 5 +++++ |
| drivers/md/dm-snap-persistent.c | 22 ++++++++++++++-------- |
| 2 files changed, 19 insertions(+), 8 deletions(-) |
| |
| --- a/drivers/md/dm-exception-store.c |
| +++ b/drivers/md/dm-exception-store.c |
| @@ -191,6 +191,11 @@ int dm_exception_store_set_chunk_size(st |
| return -EINVAL; |
| } |
| |
| + if (chunk_size_ulong > INT_MAX >> SECTOR_SHIFT) { |
| + *error = "Chunk size is too high"; |
| + return -EINVAL; |
| + } |
| + |
| store->chunk_size = chunk_size_ulong; |
| store->chunk_mask = chunk_size_ulong - 1; |
| store->chunk_shift = ffs(chunk_size_ulong) - 1; |
| --- a/drivers/md/dm-snap-persistent.c |
| +++ b/drivers/md/dm-snap-persistent.c |
| @@ -286,6 +286,7 @@ static int read_header(struct pstore *ps |
| struct disk_header *dh; |
| chunk_t chunk_size; |
| int chunk_size_supplied = 1; |
| + char *chunk_err; |
| |
| /* |
| * Use default chunk size (or hardsect_size, if larger) if none supplied |
| @@ -329,20 +330,25 @@ static int read_header(struct pstore *ps |
| ps->version = le32_to_cpu(dh->version); |
| chunk_size = le32_to_cpu(dh->chunk_size); |
| |
| - if (!chunk_size_supplied || ps->store->chunk_size == chunk_size) |
| + if (ps->store->chunk_size == chunk_size) |
| return 0; |
| |
| - DMWARN("chunk size %llu in device metadata overrides " |
| - "table chunk size of %llu.", |
| - (unsigned long long)chunk_size, |
| - (unsigned long long)ps->store->chunk_size); |
| + if (chunk_size_supplied) |
| + DMWARN("chunk size %llu in device metadata overrides " |
| + "table chunk size of %llu.", |
| + (unsigned long long)chunk_size, |
| + (unsigned long long)ps->store->chunk_size); |
| |
| /* We had a bogus chunk_size. Fix stuff up. */ |
| free_area(ps); |
| |
| - ps->store->chunk_size = chunk_size; |
| - ps->store->chunk_mask = chunk_size - 1; |
| - ps->store->chunk_shift = ffs(chunk_size) - 1; |
| + r = dm_exception_store_set_chunk_size(ps->store, chunk_size, |
| + &chunk_err); |
| + if (r) { |
| + DMERR("invalid on-disk chunk size %llu: %s.", |
| + (unsigned long long)chunk_size, chunk_err); |
| + return r; |
| + } |
| |
| r = dm_io_client_resize(sectors_to_pages(ps->store->chunk_size), |
| ps->io_client); |