osbase/blockdev: implement copy_file_range optimization

This change enables the use of the copy_file_range syscall on Linux when
copying from an os.File to a blockdev.File. This speeds up building of
system images, especially with a file system which supports reflinks.

The implementation is partially based on the implementation in the Go
standard library for copy_file_range between two os.File in
src/os/zero_copy_linux.go and src/internal/poll/copy_file_range_unix.go.
We can't use that implementation, because it only supports using the
file offset for both source and destination, but we want to provide the
destination offset as an argument. To support this, the ReaderFromAt
interface is introduced.

With these changes, copy_file_range is now used when building system
images, for both the rootfs and files on the FAT32 boot partition. If
the file system supports it (e.g. XFS), reflinks will be used for the
rootfs, which means no data is copied. For files on the FAT32 partition,
reflinks probably can't be used, because these are only aligned to 512
bytes but would need to be aligned to 4096 bytes on my system for
reflinking.

Change-Id: Ie42b5834e6d3f63a5cc1f347d2681d8a6bb5c006
Reviewed-on: https://review.monogon.dev/c/monogon/+/4293
Tested-by: Jenkins CI
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/osbase/fat32/utils.go b/osbase/fat32/utils.go
index 8ddb162..309ecf6 100644
--- a/osbase/fat32/utils.go
+++ b/osbase/fat32/utils.go
@@ -11,8 +11,6 @@
 
 // Wraps a writer and provides support for writing padding up to a specified
 // alignment.
-// TODO(lorenz): Implement WriterTo when w implements it to allow for copy
-// offload
 type blockWriter struct {
 	w io.Writer
 	n int64
@@ -28,6 +26,13 @@
 	return
 }
 
+func (b *blockWriter) ReadFrom(r io.Reader) (n int64, err error) {
+	// io.Copy forwards to b.w.ReadFrom if available.
+	n, err = io.Copy(b.w, r)
+	b.n += n
+	return
+}
+
 func (b *blockWriter) FinishBlock(alignment int64, mustZero bool) (err error) {
 	requiredBytes := (alignment - (b.n % alignment)) % alignment
 	if requiredBytes == 0 {