summaryrefslogtreecommitdiff
path: root/src/basic/copy.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-03-23 16:41:42 +0100
committerSven Eden <yamakuzure@gmx.net>2018-08-24 16:47:08 +0200
commit26e21636556e1950038226e2a5c58dbc46dced30 (patch)
treea730a7b5624697b5db66124aed67b5501802ab79 /src/basic/copy.c
parent0c28c8723ac7d39dbba1b9679fb0d4bc16d83ab0 (diff)
copy: extend copy_bytes() a bit
Optionally, when we copy between fds with simple read/write, let's return any remaining data we already read into the buffer if write fails. This is useful to allow callers to use the read data otherwise, perhaps implementing a different fallback for copying.
Diffstat (limited to 'src/basic/copy.c')
-rw-r--r--src/basic/copy.c53
1 files changed, 47 insertions, 6 deletions
diff --git a/src/basic/copy.c b/src/basic/copy.c
index 478911285..e77ddd80f 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -57,7 +57,13 @@ static ssize_t try_copy_file_range(int fd_in, loff_t *off_in,
return -errno;
}
-int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
+int copy_bytes_full(
+ int fdf, int fdt,
+ uint64_t max_bytes,
+ CopyFlags copy_flags,
+ void **ret_remains,
+ size_t *ret_remains_size) {
+
bool try_cfr = true, try_sendfile = true, try_splice = true;
int r;
size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */
@@ -67,7 +73,16 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
/* Tries to copy bytes from the file descriptor 'fdf' to 'fdt' in the smartest possible way. Copies a maximum
* of 'max_bytes', which may be specified as UINT64_MAX, in which no maximum is applied. Returns negative on
- * error, zero if EOF is hit before the bytes limit is hit and positive otherwise. */
+ * error, zero if EOF is hit before the bytes limit is hit and positive otherwise. If the copy fails for some
+ * reason but we read but didn't yet write some data an ret_remains/ret_remains_size is not NULL, then it will
+ * be initialized with an allocated buffer containing this "remaining" data. Note that these two parameters are
+ * initialized with a valid buffer only on failure and only if there's actually data already read. Otherwise
+ * these parameters if non-NULL are set to NULL. */
+
+ if (ret_remains)
+ *ret_remains = NULL;
+ if (ret_remains_size)
+ *ret_remains_size = 0;
#if 0 /// UNNEEDED by elogind
/* Try btrfs reflinks first. This only works on regular, seekable files, hence let's check the file offsets of
@@ -187,7 +202,8 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
/* As a fallback just copy bits by hand */
{
- uint8_t buf[MIN(m, COPY_BUFFER_SIZE)];
+ uint8_t buf[MIN(m, COPY_BUFFER_SIZE)], *p = buf;
+ ssize_t z;
n = read(fdf, buf, sizeof buf);
if (n < 0)
@@ -195,9 +211,34 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
if (n == 0) /* EOF */
break;
- r = loop_write(fdt, buf, (size_t) n, false);
- if (r < 0)
- return r;
+ z = (size_t) n;
+ do {
+ ssize_t k;
+
+ k = write(fdt, p, z);
+ if (k < 0) {
+ r = -errno;
+
+ if (ret_remains) {
+ void *copy;
+
+ copy = memdup(p, z);
+ if (!copy)
+ return -ENOMEM;
+
+ *ret_remains = copy;
+ }
+
+ if (ret_remains_size)
+ *ret_remains_size = z;
+
+ return r;
+ }
+
+ assert(k <= z);
+ z -= k;
+ p += k;
+ } while (z > 0);
}
next: