diff options
author | Dmitry Monakhov <dmonakhov@openvz.org> | 2015-10-30 22:04:46 +0000 |
---|---|---|
committer | Pádraig Brady <P@draigBrady.com> | 2015-11-24 00:49:03 +0000 |
commit | eea6b49210edf69682b2d0606bee17bbccb3765b (patch) | |
tree | 84e120fb2c31c0345b729a8ffe731e477ff4db78 /src | |
parent | 6df26278d6cf5c6fad04d3fbaed4c06a223fb0bd (diff) | |
download | coreutils-eea6b49210edf69682b2d0606bee17bbccb3765b.tar.xz |
copy: fix copying of extents beyond the apparent file size
fallocate can allocate extents beyond EOF via FALLOC_FL_KEEP_SIZE.
Where there is a gap (hole) between the extents, and EOF is within
that gap, the final hole wasn't reproduced, resulting in silent
data corruption in the copied file (size too small).
* src/copy.c (extent_copy): Ensure we don't process extents
beyond the apparent file size, since processing and allocating
those is not currently supported.
* tests/cp/fiemap-extents.sh: Renamed from tests/cp/fiemap-empty.sh
and re-enable parts checking the extents at and beyond EOF.
* tests/local.mk: Reference the renamed test.
* NEWS: Mention the bug fix.
Fixes http://bugs.gnu.org/21790
Diffstat (limited to 'src')
-rw-r--r-- | src/copy.c | 19 |
1 files changed, 18 insertions, 1 deletions
diff --git a/src/copy.c b/src/copy.c index dc1cd2904..6771bb53b 100644 --- a/src/copy.c +++ b/src/copy.c @@ -432,6 +432,20 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, ext_len = 0; } + /* Truncate extent to EOF. Extents starting after EOF are + treated as zero length extents starting right after EOF. + Generally this will trigger with an extent starting after + src_total_size, and result in creating a hole or zeros until EOF. + Though in a file in which extents have changed since src_total_size + was determined, we might have an extent spanning that size, + in which case we'll only copy data up to that size. */ + if (src_total_size < ext_start + ext_len) + { + if (src_total_size < ext_start) + ext_start = src_total_size; + ext_len = src_total_size - ext_start; + } + ext_hole_size = ext_start - last_ext_start - last_ext_len; wrote_hole_at_eof = false; @@ -495,14 +509,17 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, off_t n_read; empty_extent = false; last_ext_len = ext_len; + bool read_hole; if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size, sparse_mode == SPARSE_ALWAYS ? hole_size: 0, true, src_name, dst_name, ext_len, &n_read, - &wrote_hole_at_eof)) + &read_hole)) goto fail; dest_pos = ext_start + n_read; + if (n_read) + wrote_hole_at_eof = read_hole; } /* If the file ends with unwritten extents not accounted for in the |