]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
copy: when looking for file holes, consider empty data segments
authorLennart Poettering <lennart@poettering.net>
Fri, 20 Jun 2025 10:07:27 +0000 (12:07 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 20 Jun 2025 17:19:27 +0000 (02:19 +0900)
This could mean that we hit EOF, or it could mean that somebody punched
a hole concurrently where we are currently looking. Let's figure this
out by simply trying to copy a single byte, which will give us a
definitive answer.

Fixes: #35569
src/shared/copy.c

index 17268387569fa57ef188ed4a3b3ef44029aaf75c..6f415fe2078f27345b53fc7abd51dab83dc99167 100644 (file)
@@ -335,15 +335,21 @@ int copy_bytes_full(
                                         break;
                                 return -errno;
                         }
+                        if (e == c)
+                                /* Empty data segment? Maybe concurrent hole punching taking place? Or EOF?
+                                 * Let's figure this out by copying the smallest amount possible */
+                                m = 1;
+                        else {
+                                assert(e > c);
+
+                                /* SEEK_HOLE modifies the file offset so we need to move back to the initial offset. */
+                                if (lseek(fdf, c, SEEK_SET) < 0)
+                                        return -errno;
 
-                        /* SEEK_HOLE modifies the file offset so we need to move back to the initial offset. */
-                        if (lseek(fdf, c, SEEK_SET) < 0)
-                                return -errno;
-
-                        /* Make sure we're not copying more than the current data segment. */
-                        m = MIN(m, (size_t) e - c);
-                        if (m <= 0)
-                                continue;
+                                /* Make sure we're not copying more than the current data segment. */
+                                m = MIN(m, (size_t) e - c);
+                                assert(m > 0);
+                        }
                 }
 
                 /* First try copy_file_range(), unless we already tried */