diff options
author | Pádraig Brady <P@draigBrady.com> | 2011-03-28 19:22:21 +0100 |
---|---|---|
committer | Pádraig Brady <P@draigBrady.com> | 2011-04-01 14:42:46 +0100 |
commit | b56b53bd70b1f8fa2b5a95d4569bb72a2419b5cd (patch) | |
tree | badf81e4bcc92cc77b8359aafb955a8a05ae9c8f /src | |
parent | 594292a1d89332dd006cd7fe3116cf46ebe9acf6 (diff) | |
download | coreutils-b56b53bd70b1f8fa2b5a95d4569bb72a2419b5cd.tar.xz |
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: Mention the fix
Diffstat (limited to 'src')
-rw-r--r-- | src/extent-scan.c | 34 |
1 files changed, 33 insertions, 1 deletions
diff --git a/src/extent-scan.c b/src/extent-scan.c index f10d8e0a0..c0a5de6c2 100644 --- a/src/extent-scan.c +++ b/src/extent-scan.c @@ -122,7 +122,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) @@ -134,6 +134,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; |