diff options
author | Mikulas Patocka <mpatocka@redhat.com> | 2009-09-04 15:40:43 -0400 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2009-09-04 15:40:43 -0400 |
commit | ae0b7448e91353ea5f821601a055aca6b58042cd (patch) | |
tree | a5c5d0532c808fb1eb2c01414edfcd2d0c039a7e | |
parent | 2defcc3fb4661e7351cb2ac48d843efc4c64db13 (diff) |
dm snapshot: fix on disk chunk size validation
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 .
Cc: stable@kernel.org
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
-rw-r--r-- | drivers/md/dm-exception-store.c | 5 | ||||
-rw-r--r-- | drivers/md/dm-snap-persistent.c | 22 |
2 files changed, 19 insertions, 8 deletions
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index 4c01c7535fb5..556acff3952f 100644 --- 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(struct dm_exception_store *store, | |||
191 | return -EINVAL; | 191 | return -EINVAL; |
192 | } | 192 | } |
193 | 193 | ||
194 | if (chunk_size_ulong > INT_MAX >> SECTOR_SHIFT) { | ||
195 | *error = "Chunk size is too high"; | ||
196 | return -EINVAL; | ||
197 | } | ||
198 | |||
194 | store->chunk_size = chunk_size_ulong; | 199 | store->chunk_size = chunk_size_ulong; |
195 | store->chunk_mask = chunk_size_ulong - 1; | 200 | store->chunk_mask = chunk_size_ulong - 1; |
196 | store->chunk_shift = ffs(chunk_size_ulong) - 1; | 201 | store->chunk_shift = ffs(chunk_size_ulong) - 1; |
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 5d1a97580cb7..d5b2e08750d5 100644 --- 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, int *new_snapshot) | |||
286 | struct disk_header *dh; | 286 | struct disk_header *dh; |
287 | chunk_t chunk_size; | 287 | chunk_t chunk_size; |
288 | int chunk_size_supplied = 1; | 288 | int chunk_size_supplied = 1; |
289 | char *chunk_err; | ||
289 | 290 | ||
290 | /* | 291 | /* |
291 | * Use default chunk size (or hardsect_size, if larger) if none supplied | 292 | * Use default chunk size (or hardsect_size, if larger) if none supplied |
@@ -329,20 +330,25 @@ static int read_header(struct pstore *ps, int *new_snapshot) | |||
329 | ps->version = le32_to_cpu(dh->version); | 330 | ps->version = le32_to_cpu(dh->version); |
330 | chunk_size = le32_to_cpu(dh->chunk_size); | 331 | chunk_size = le32_to_cpu(dh->chunk_size); |
331 | 332 | ||
332 | if (!chunk_size_supplied || ps->store->chunk_size == chunk_size) | 333 | if (ps->store->chunk_size == chunk_size) |
333 | return 0; | 334 | return 0; |
334 | 335 | ||
335 | DMWARN("chunk size %llu in device metadata overrides " | 336 | if (chunk_size_supplied) |
336 | "table chunk size of %llu.", | 337 | DMWARN("chunk size %llu in device metadata overrides " |
337 | (unsigned long long)chunk_size, | 338 | "table chunk size of %llu.", |
338 | (unsigned long long)ps->store->chunk_size); | 339 | (unsigned long long)chunk_size, |
340 | (unsigned long long)ps->store->chunk_size); | ||
339 | 341 | ||
340 | /* We had a bogus chunk_size. Fix stuff up. */ | 342 | /* We had a bogus chunk_size. Fix stuff up. */ |
341 | free_area(ps); | 343 | free_area(ps); |
342 | 344 | ||
343 | ps->store->chunk_size = chunk_size; | 345 | r = dm_exception_store_set_chunk_size(ps->store, chunk_size, |
344 | ps->store->chunk_mask = chunk_size - 1; | 346 | &chunk_err); |
345 | ps->store->chunk_shift = ffs(chunk_size) - 1; | 347 | if (r) { |
348 | DMERR("invalid on-disk chunk size %llu: %s.", | ||
349 | (unsigned long long)chunk_size, chunk_err); | ||
350 | return r; | ||
351 | } | ||
346 | 352 | ||
347 | r = dm_io_client_resize(sectors_to_pages(ps->store->chunk_size), | 353 | r = dm_io_client_resize(sectors_to_pages(ps->store->chunk_size), |
348 | ps->io_client); | 354 | ps->io_client); |