>From e409ab13bc9ffb1e34f7e2fe41280ce373e8dfbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Mon, 28 Mar 2011 19:22:21 +0100 Subject: [PATCH] copy: protect against overlapping extents * src/extent-scan.c (extent_scan_read): Add a more stringent check for OFF_T overflow, to ensure subsequent code is immune. Detect overlapping extents and adjust, so as files always copied. Detection using a single scan with fallback to a standard copy was thought too expensive in memory or time. --- NEWS | 4 ++++ src/extent-scan.c | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletions(-) diff --git a/NEWS b/NEWS index 5b418bd..9c4a16f 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,10 @@ GNU coreutils NEWS -*- outline -*- wc would dereference a NULL pointer upon an early out-of-memory error [bug introduced in coreutils-7.1] + cp now avoids FIEMAP issues with BTRFS before Linux 2.6.38, + which could result in corrupt copies of sparse files. + [bug introduced in coreutils-8.10] + ** Changes in behavior cp now copies empty extents efficiently on file systems with FIEMAP diff --git a/src/extent-scan.c b/src/extent-scan.c index b9520b7..24b56b8 100644 --- a/src/extent-scan.c +++ b/src/extent-scan.c @@ -90,7 +90,7 @@ extent_scan_read (struct extent_scan *scan) for (i = 0; i < scan->ei_count; i++) { - assert (fm_extents[i].fe_logical <= OFF_T_MAX); + assert (fm_extents[i].fe_logical <= OFF_T_MAX - fm_extents[i].fe_length); if (si && last_ei->ext_flags == (fm_extents[i].fe_flags & ~FIEMAP_EXTENT_LAST) @@ -102,6 +102,38 @@ extent_scan_read (struct extent_scan *scan) /* Copy flags in case different. */ last_ei->ext_flags = fm_extents[i].fe_flags; } + else if ((si == 0 && scan->scan_start > fm_extents[i].fe_logical) + || (si && last_ei->ext_logical + last_ei->ext_length > + fm_extents[i].fe_logical)) + { + /* BTRFS before 2.6.38 could return overlapping extents + for sparse files. We adjust the returned extents + rather than failing, as otherwise it would be inefficient + to detect this on the initial scan. */ + uint64_t new_logical; + uint64_t length_adjust; + if (si == 0) + new_logical = scan->scan_start; + else + { + /* We could return here if scan->scan_start == 0 + but don't so as to minimize special cases. */ + new_logical = last_ei->ext_logical + last_ei->ext_length; + } + length_adjust = new_logical - fm_extents[i].fe_logical; + /* If an extent is contained within the previous one, just fail. */ + if (length_adjust < fm_extents[i].fe_length) + { + if (scan->scan_start == 0) + scan->initial_scan_failed = true; + return false; + } + fm_extents[i].fe_logical = new_logical; + fm_extents[i].fe_length -= length_adjust; + /* Process the adjusted extent again. */ + i--; + continue; + } else { last_ei = scan->ext_info + si; -- 1.7.4