>From 51981008f9892d44231c432535deac4f9b3cbe5e Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 23 Jun 2020 19:18:04 -0700 Subject: [PATCH 2/5] cp: refactor extent_copy * src/copy.c (extent_copy): New arg SCAN, replacing REQUIRE_NORMAL_COPY. All callers changed. (enum scantype): New type. (infer_scantype): Rename from is_probably_sparse and return the new type. Add args FD and SCAN. All callers changed. --- src/copy.c | 119 +++++++++++++++++++++++++---------------------------- 1 file changed, 55 insertions(+), 64 deletions(-) diff --git a/src/copy.c b/src/copy.c index 54601ce07..f694f913f 100644 --- a/src/copy.c +++ b/src/copy.c @@ -422,9 +422,8 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, size_t hole_size, off_t src_total_size, enum Sparse_type sparse_mode, char const *src_name, char const *dst_name, - bool *require_normal_copy) + struct extent_scan *scan) { - struct extent_scan scan; off_t last_ext_start = 0; off_t last_ext_len = 0; @@ -432,45 +431,25 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, We may need this at the end, for a final ftruncate. */ off_t dest_pos = 0; - extent_scan_init (src_fd, &scan); - - *require_normal_copy = false; bool wrote_hole_at_eof = true; - do + while (true) { - bool ok = extent_scan_read (&scan); - if (! ok) - { - if (scan.hit_final_extent) - break; - - if (scan.initial_scan_failed) - { - *require_normal_copy = true; - return false; - } - - error (0, errno, _("%s: failed to get extents info"), - quotef (src_name)); - return false; - } - bool empty_extent = false; - for (unsigned int i = 0; i < scan.ei_count || empty_extent; i++) + for (unsigned int i = 0; i < scan->ei_count || empty_extent; i++) { off_t ext_start; off_t ext_len; off_t ext_hole_size; - if (i < scan.ei_count) + if (i < scan->ei_count) { - ext_start = scan.ext_info[i].ext_logical; - ext_len = scan.ext_info[i].ext_length; + ext_start = scan->ext_info[i].ext_logical; + ext_len = scan->ext_info[i].ext_length; } else /* empty extent at EOF. */ { i--; - ext_start = last_ext_start + scan.ext_info[i].ext_length; + ext_start = last_ext_start + scan->ext_info[i].ext_length; ext_len = 0; } @@ -498,7 +477,7 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, { error (0, errno, _("cannot lseek %s"), quoteaf (src_name)); fail: - extent_scan_free (&scan); + extent_scan_free (scan); return false; } @@ -539,7 +518,7 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, /* For now, do not treat FIEMAP_EXTENT_UNWRITTEN specially, because that (in combination with no sync) would lead to data loss at least on XFS and ext4 when using 2.6.39-rc3 kernels. */ - if (0 && (scan.ext_info[i].ext_flags & FIEMAP_EXTENT_UNWRITTEN)) + if (0 && (scan->ext_info[i].ext_flags & FIEMAP_EXTENT_UNWRITTEN)) { empty_extent = true; last_ext_len = 0; @@ -571,16 +550,23 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, extents beyond the apparent size. */ if (dest_pos == src_total_size) { - scan.hit_final_extent = true; + scan->hit_final_extent = true; break; } } /* Release the space allocated to scan->ext_info. */ - extent_scan_free (&scan); + extent_scan_free (scan); + if (scan->hit_final_extent) + break; + if (! extent_scan_read (scan) && ! scan->hit_final_extent) + { + error (0, errno, _("%s: failed to get extents info"), + quotef (src_name)); + return false; + } } - while (! scan.hit_final_extent); /* When the source file ends with a hole, we have to do a little more work, since the above copied only up to and including the final extent. @@ -1021,16 +1007,35 @@ fchmod_or_lchmod (int desc, char const *name, mode_t mode) # define HAVE_STRUCT_STAT_ST_BLOCKS 0 #endif +/* Type of scan being done on the input when looking for sparseness. */ +enum scantype + { + /* No fancy scanning; just read and write. */ + PLAIN_SCANTYPE, + + /* Read and examine data looking for zero blocks; useful when + attempting to create sparse output. */ + ZERO_SCANTYPE, + + /* Extent information is available. */ + EXTENT_SCANTYPE + }; + /* Use a heuristic to determine whether stat buffer SB comes from a file with sparse blocks. If the file has fewer blocks than would normally be needed for a file of its size, then at least one of the blocks in the file is a hole. In that case, return true. */ -static bool -is_probably_sparse (struct stat const *sb) +static enum scantype +infer_scantype (int fd, struct stat const *sb, struct extent_scan *scan) { - return (HAVE_STRUCT_STAT_ST_BLOCKS - && S_ISREG (sb->st_mode) - && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE); + if (! (HAVE_STRUCT_STAT_ST_BLOCKS + && S_ISREG (sb->st_mode) + && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE)) + return PLAIN_SCANTYPE; + + extent_scan_init (fd, scan); + extent_scan_read (scan); + return scan->initial_scan_failed ? ZERO_SCANTYPE : EXTENT_SCANTYPE; } @@ -1061,6 +1066,7 @@ copy_reg (char const *src_name, char const *dst_name, mode_t src_mode = src_sb->st_mode; struct stat sb; struct stat src_open_sb; + struct extent_scan scan; bool return_val = true; bool data_copy_required = x->data_copy_required; @@ -1260,23 +1266,13 @@ copy_reg (char const *src_name, char const *dst_name, fdadvise (source_desc, 0, 0, FADVISE_SEQUENTIAL); /* Deal with sparse files. */ - bool make_holes = false; - bool sparse_src = is_probably_sparse (&src_open_sb); - - if (S_ISREG (sb.st_mode)) - { - /* Even with --sparse=always, try to create holes only - if the destination is a regular file. */ - if (x->sparse_mode == SPARSE_ALWAYS) - make_holes = true; - - /* Use a heuristic to determine whether SRC_NAME contains any sparse - blocks. If the file has fewer blocks than would normally be - needed for a file of its size, then at least one of the blocks in - the file is a hole. */ - if (x->sparse_mode == SPARSE_AUTO && sparse_src) - make_holes = true; - } + enum scantype scantype = infer_scantype (source_desc, &src_open_sb, + &scan); + bool make_holes + = (S_ISREG (sb.st_mode) + && (x->sparse_mode == SPARSE_ALWAYS + || (x->sparse_mode == SPARSE_AUTO + && scantype != PLAIN_SCANTYPE))); /* If not making a sparse file, try to use a more-efficient buffer size. */ @@ -1305,10 +1301,8 @@ copy_reg (char const *src_name, char const *dst_name, buf_alloc = xmalloc (buf_size + buf_alignment); buf = ptr_align (buf_alloc, buf_alignment); - if (sparse_src) + if (scantype == EXTENT_SCANTYPE) { - bool normal_copy_required; - /* Perform an efficient extent-based copy, falling back to the standard copy only if the initial extent scan fails. If the '--sparse=never' option is specified, write all data but use @@ -1316,14 +1310,11 @@ copy_reg (char const *src_name, char const *dst_name, if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size, src_open_sb.st_size, make_holes ? x->sparse_mode : SPARSE_NEVER, - src_name, dst_name, &normal_copy_required)) + src_name, dst_name, &scan)) goto preserve_metadata; - if (! normal_copy_required) - { - return_val = false; - goto close_src_and_dst_desc; - } + return_val = false; + goto close_src_and_dst_desc; } off_t n_read; -- 2.25.4