aboutsummaryrefslogtreecommitdiff
path: root/lib/portability.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/portability.c')
-rw-r--r--lib/portability.c35
1 files changed, 27 insertions, 8 deletions
diff --git a/lib/portability.c b/lib/portability.c
index 5f98138c..89744dd0 100644
--- a/lib/portability.c
+++ b/lib/portability.c
@@ -623,26 +623,45 @@ int get_block_device_size(int fd, unsigned long long* size)
}
#endif
-// TODO copy_file_range
+static ssize_t copy_file_range_wrap(int infd, off_t *inoff, int outfd,
+ off_t *outoff, size_t len, unsigned flags)
+{
+ // glibc added this constant in git at the end of 2017, shipped in 2018-02.
+#if defined(__NR_copy_file_range)
+ return syscall(__NR_copy_file_range, infd, inoff, outfd, outoff, len, flags);
+#else
+ errno = EINVAL;
+ return -1;
+#endif
+}
+
// Return bytes copied from in to out. If bytes <0 copy all of in to out.
-// If consuemd isn't null, amount read saved there (return is written or error)
+// If consumed isn't null, amount read saved there (return is written or error)
long long sendfile_len(int in, int out, long long bytes, long long *consumed)
{
long long total = 0, len, ww;
+ int copy_file_range = CFG_TOYBOX_COPYFILERANGE;
if (consumed) *consumed = 0;
if (in<0) return 0;
while (bytes != total) {
ww = 0;
len = bytes-total;
- if (bytes<0 || len>sizeof(libbuf)) len = sizeof(libbuf);
errno = 0;
-#if CFG_TOYBOX_COPYFILERANGE
- len = copy_file_range(in, 0, out, 0, bytes, 0);
-#else
- ww = len = read(in, libbuf, len);
-#endif
+ if (copy_file_range) {
+ if (bytes<0 || bytes>(1<<30)) len = (1<<30);
+ len = copy_file_range_wrap(in, 0, out, 0, len, 0);
+ if (len < 0 && errno == EINVAL) {
+ copy_file_range = 0;
+
+ continue;
+ }
+ }
+ if (!copy_file_range) {
+ if (bytes<0 || len>sizeof(libbuf)) len = sizeof(libbuf);
+ ww = len = read(in, libbuf, len);
+ }
if (len<1 && errno==EAGAIN) continue;
if (len<1) break;
if (consumed) *consumed += len;