summary refs log tree commit diff
path: root/nix
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2024-05-13 12:02:30 +0200
committerLudovic Courtès <ludo@gnu.org>2024-05-13 16:31:34 +0200
commit7757fdd491862fa5c33f1f894503346b89898a01 (patch)
tree52f5ae8916c7eca875e917f56c43007a48d3c7f8 /nix
parentbe235a3092c2b1df413bab02f345050f6b0f15fd (diff)
downloadguix-7757fdd491862fa5c33f1f894503346b89898a01.tar.gz
daemon: Loop over ‘copy_file_range’ upon short writes.
Fixes <https://issues.guix.gnu.org/70877>.

* nix/libutil/util.cc (copyFile): Loop over ‘copy_file_range’ instead of
throwing upon short write.

Reported-by: Ricardo Wurmus <rekado@elephly.net>
Change-Id: Id7b8a65ea59006c2d91bc23732309a68665b9ca0
Diffstat (limited to 'nix')
-rw-r--r--nix/libutil/util.cc11
1 files changed, 8 insertions, 3 deletions
diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc
index 578d657293..3206dea11b 100644
--- a/nix/libutil/util.cc
+++ b/nix/libutil/util.cc
@@ -397,9 +397,14 @@ static void copyFile(int sourceFd, int destinationFd)
     } else {
 	if (result < 0)
 	    throw SysError(format("copy_file_range `%1%' to `%2%'") % sourceFd % destinationFd);
-	if (result < st.st_size)
-	    throw SysError(format("short write in copy_file_range `%1%' to `%2%'")
-			   % sourceFd % destinationFd);
+
+	/* If 'copy_file_range' copied less than requested, try again.  */
+	for (ssize_t copied = result; copied < st.st_size; copied += result) {
+	    result = copy_file_range(sourceFd, NULL, destinationFd, NULL,
+				     st.st_size - copied, 0);
+	    if (result < 0)
+		throw SysError(format("copy_file_range `%1%' to `%2%'") % sourceFd % destinationFd);
+	}
     }
 }