aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md/dm-thin.c
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2013-03-01 17:45:44 -0500
committerAlasdair G Kergon <agk@redhat.com>2013-03-01 17:45:44 -0500
commitfd7c092e711ebab55b2688d3859d95dfd0301f73 (patch)
tree3cc99f96f4a2de8e22347feb86b0ecd5dd7200d0 /drivers/md/dm-thin.c
parent16245bdc9d3e22d1460341a655c8b5288953bc14 (diff)
dm: fix truncated status strings
Avoid returning a truncated table or status string instead of setting the DM_BUFFER_FULL_FLAG when the last target of a table fills the buffer. When processing a table or status request, the function retrieve_status calls ti->type->status. If ti->type->status returns non-zero, retrieve_status assumes that the buffer overflowed and sets DM_BUFFER_FULL_FLAG. However, targets don't return non-zero values from their status method on overflow. Most targets returns always zero. If a buffer overflow happens in a target that is not the last in the table, it gets noticed during the next iteration of the loop in retrieve_status; but if a buffer overflow happens in the last target, it goes unnoticed and erroneously truncated data is returned. In the current code, the targets behave in the following way: * dm-crypt returns -ENOMEM if there is not enough space to store the key, but it returns 0 on all other overflows. * dm-thin returns errors from the status method if a disk error happened. This is incorrect because retrieve_status doesn't check the error code, it assumes that all non-zero values mean buffer overflow. * all the other targets always return 0. This patch changes the ti->type->status function to return void (because most targets don't use the return code). Overflow is detected in retrieve_status: if the status method fills up the remaining space completely, it is assumed that buffer overflow happened. Cc: stable@vger.kernel.org Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md/dm-thin.c')
-rw-r--r--drivers/md/dm-thin.c80
1 files changed, 49 insertions, 31 deletions
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 5409607d4875..7a66d73148e6 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -2299,8 +2299,8 @@ static void emit_flags(struct pool_features *pf, char *result,
2299 * <transaction id> <used metadata sectors>/<total metadata sectors> 2299 * <transaction id> <used metadata sectors>/<total metadata sectors>
2300 * <used data sectors>/<total data sectors> <held metadata root> 2300 * <used data sectors>/<total data sectors> <held metadata root>
2301 */ 2301 */
2302static int pool_status(struct dm_target *ti, status_type_t type, 2302static void pool_status(struct dm_target *ti, status_type_t type,
2303 unsigned status_flags, char *result, unsigned maxlen) 2303 unsigned status_flags, char *result, unsigned maxlen)
2304{ 2304{
2305 int r; 2305 int r;
2306 unsigned sz = 0; 2306 unsigned sz = 0;
@@ -2326,32 +2326,41 @@ static int pool_status(struct dm_target *ti, status_type_t type,
2326 if (!(status_flags & DM_STATUS_NOFLUSH_FLAG) && !dm_suspended(ti)) 2326 if (!(status_flags & DM_STATUS_NOFLUSH_FLAG) && !dm_suspended(ti))
2327 (void) commit_or_fallback(pool); 2327 (void) commit_or_fallback(pool);
2328 2328
2329 r = dm_pool_get_metadata_transaction_id(pool->pmd, 2329 r = dm_pool_get_metadata_transaction_id(pool->pmd, &transaction_id);
2330 &transaction_id); 2330 if (r) {
2331 if (r) 2331 DMERR("dm_pool_get_metadata_transaction_id returned %d", r);
2332 return r; 2332 goto err;
2333 }
2333 2334
2334 r = dm_pool_get_free_metadata_block_count(pool->pmd, 2335 r = dm_pool_get_free_metadata_block_count(pool->pmd, &nr_free_blocks_metadata);
2335 &nr_free_blocks_metadata); 2336 if (r) {
2336 if (r) 2337 DMERR("dm_pool_get_free_metadata_block_count returned %d", r);
2337 return r; 2338 goto err;
2339 }
2338 2340
2339 r = dm_pool_get_metadata_dev_size(pool->pmd, &nr_blocks_metadata); 2341 r = dm_pool_get_metadata_dev_size(pool->pmd, &nr_blocks_metadata);
2340 if (r) 2342 if (r) {
2341 return r; 2343 DMERR("dm_pool_get_metadata_dev_size returned %d", r);
2344 goto err;
2345 }
2342 2346
2343 r = dm_pool_get_free_block_count(pool->pmd, 2347 r = dm_pool_get_free_block_count(pool->pmd, &nr_free_blocks_data);
2344 &nr_free_blocks_data); 2348 if (r) {
2345 if (r) 2349 DMERR("dm_pool_get_free_block_count returned %d", r);
2346 return r; 2350 goto err;
2351 }
2347 2352
2348 r = dm_pool_get_data_dev_size(pool->pmd, &nr_blocks_data); 2353 r = dm_pool_get_data_dev_size(pool->pmd, &nr_blocks_data);
2349 if (r) 2354 if (r) {
2350 return r; 2355 DMERR("dm_pool_get_data_dev_size returned %d", r);
2356 goto err;
2357 }
2351 2358
2352 r = dm_pool_get_metadata_snap(pool->pmd, &held_root); 2359 r = dm_pool_get_metadata_snap(pool->pmd, &held_root);
2353 if (r) 2360 if (r) {
2354 return r; 2361 DMERR("dm_pool_get_metadata_snap returned %d", r);
2362 goto err;
2363 }
2355 2364
2356 DMEMIT("%llu %llu/%llu %llu/%llu ", 2365 DMEMIT("%llu %llu/%llu %llu/%llu ",
2357 (unsigned long long)transaction_id, 2366 (unsigned long long)transaction_id,
@@ -2388,8 +2397,10 @@ static int pool_status(struct dm_target *ti, status_type_t type,
2388 emit_flags(&pt->requested_pf, result, sz, maxlen); 2397 emit_flags(&pt->requested_pf, result, sz, maxlen);
2389 break; 2398 break;
2390 } 2399 }
2400 return;
2391 2401
2392 return 0; 2402err:
2403 DMEMIT("Error");
2393} 2404}
2394 2405
2395static int pool_iterate_devices(struct dm_target *ti, 2406static int pool_iterate_devices(struct dm_target *ti,
@@ -2468,7 +2479,7 @@ static struct target_type pool_target = {
2468 .name = "thin-pool", 2479 .name = "thin-pool",
2469 .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE | 2480 .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
2470 DM_TARGET_IMMUTABLE, 2481 DM_TARGET_IMMUTABLE,
2471 .version = {1, 6, 0}, 2482 .version = {1, 6, 1},
2472 .module = THIS_MODULE, 2483 .module = THIS_MODULE,
2473 .ctr = pool_ctr, 2484 .ctr = pool_ctr,
2474 .dtr = pool_dtr, 2485 .dtr = pool_dtr,
@@ -2676,8 +2687,8 @@ static void thin_postsuspend(struct dm_target *ti)
2676/* 2687/*
2677 * <nr mapped sectors> <highest mapped sector> 2688 * <nr mapped sectors> <highest mapped sector>
2678 */ 2689 */
2679static int thin_status(struct dm_target *ti, status_type_t type, 2690static void thin_status(struct dm_target *ti, status_type_t type,
2680 unsigned status_flags, char *result, unsigned maxlen) 2691 unsigned status_flags, char *result, unsigned maxlen)
2681{ 2692{
2682 int r; 2693 int r;
2683 ssize_t sz = 0; 2694 ssize_t sz = 0;
@@ -2687,7 +2698,7 @@ static int thin_status(struct dm_target *ti, status_type_t type,
2687 2698
2688 if (get_pool_mode(tc->pool) == PM_FAIL) { 2699 if (get_pool_mode(tc->pool) == PM_FAIL) {
2689 DMEMIT("Fail"); 2700 DMEMIT("Fail");
2690 return 0; 2701 return;
2691 } 2702 }
2692 2703
2693 if (!tc->td) 2704 if (!tc->td)
@@ -2696,12 +2707,16 @@ static int thin_status(struct dm_target *ti, status_type_t type,
2696 switch (type) { 2707 switch (type) {
2697 case STATUSTYPE_INFO: 2708 case STATUSTYPE_INFO:
2698 r = dm_thin_get_mapped_count(tc->td, &mapped); 2709 r = dm_thin_get_mapped_count(tc->td, &mapped);
2699 if (r) 2710 if (r) {
2700 return r; 2711 DMERR("dm_thin_get_mapped_count returned %d", r);
2712 goto err;
2713 }
2701 2714
2702 r = dm_thin_get_highest_mapped_block(tc->td, &highest); 2715 r = dm_thin_get_highest_mapped_block(tc->td, &highest);
2703 if (r < 0) 2716 if (r < 0) {
2704 return r; 2717 DMERR("dm_thin_get_highest_mapped_block returned %d", r);
2718 goto err;
2719 }
2705 2720
2706 DMEMIT("%llu ", mapped * tc->pool->sectors_per_block); 2721 DMEMIT("%llu ", mapped * tc->pool->sectors_per_block);
2707 if (r) 2722 if (r)
@@ -2721,7 +2736,10 @@ static int thin_status(struct dm_target *ti, status_type_t type,
2721 } 2736 }
2722 } 2737 }
2723 2738
2724 return 0; 2739 return;
2740
2741err:
2742 DMEMIT("Error");
2725} 2743}
2726 2744
2727static int thin_iterate_devices(struct dm_target *ti, 2745static int thin_iterate_devices(struct dm_target *ti,
@@ -2748,7 +2766,7 @@ static int thin_iterate_devices(struct dm_target *ti,
2748 2766
2749static struct target_type thin_target = { 2767static struct target_type thin_target = {
2750 .name = "thin", 2768 .name = "thin",
2751 .version = {1, 7, 0}, 2769 .version = {1, 7, 1},
2752 .module = THIS_MODULE, 2770 .module = THIS_MODULE,
2753 .ctr = thin_ctr, 2771 .ctr = thin_ctr,
2754 .dtr = thin_dtr, 2772 .dtr = thin_dtr,