summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2011-03-28 19:22:21 +0100
committerPádraig Brady <P@draigBrady.com>2011-04-01 14:42:46 +0100
commitb56b53bd70b1f8fa2b5a95d4569bb72a2419b5cd (patch)
treebadf81e4bcc92cc77b8359aafb955a8a05ae9c8f /src
parent594292a1d89332dd006cd7fe3116cf46ebe9acf6 (diff)
downloadcoreutils-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.c34
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;