Current copy-up is not efficient for sparse file, It's not only slow but also wasting more disk space when the target lower file has huge hole inside. This patch tries to recognize file hole and skip it during copy-up. In detail, this optimization checks the hole according to copy-up chunk size so it may not recognize all kind of holes in the file. However, it is easy to implement and will be enough for most of the time. Additionally, this optimization relies on lseek(2) SEEK_DATA implementation, so for some specific filesystems which do not support this feature will behave as before on copy-up. Signed-off-by: Chengguang Xu <cgxu519@xxxxxxxxxxxx> --- fs/overlayfs/copy_up.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index b801c6353100..028033c9f021 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -144,10 +144,11 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len) goto out; /* Couldn't clone, so now we try to copy the data */ - /* FIXME: copy up sparse files efficiently */ while (len) { size_t this_len = OVL_COPY_UP_CHUNK_SIZE; long bytes; + loff_t old_next_data_pos; + loff_t hole_len; if (len < this_len) this_len = len; @@ -157,6 +158,18 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len) break; } + old_next_data_pos = vfs_llseek(old_file, old_pos, SEEK_DATA); + if (old_next_data_pos >= old_pos + OVL_COPY_UP_CHUNK_SIZE) { + hole_len = (old_next_data_pos - old_pos) / + OVL_COPY_UP_CHUNK_SIZE * OVL_COPY_UP_CHUNK_SIZE; + old_pos += hole_len; + new_pos += hole_len; + len -= hole_len; + continue; + } else if (old_next_data_pos == -ENXIO) { + break; + } + bytes = do_splice_direct(old_file, &old_pos, new_file, &new_pos, this_len, SPLICE_F_MOVE); -- 2.21.0