Add splice, tee, and vmsplice.

Change-Id: I5f43380b88d776a8bb607b47dbbc5db5a2fe6163
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 839cfb7..b4c8134 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -285,6 +285,10 @@
 int     sysinfo(struct sysinfo*)  all
 int     personality(unsigned long)  all
 
+ssize_t tee(int, int, size_t, unsigned int)  all
+ssize_t splice(int, off64_t*, int, off64_t*, size_t, unsigned int)  all
+ssize_t vmsplice(int, const struct iovec*, size_t, unsigned int)  all
+
 int epoll_create1(int)  all
 int epoll_ctl(int, int op, int, struct epoll_event*)  all
 int __epoll_pwait:epoll_pwait(int, struct epoll_event*, int, int, const sigset_t*, size_t)  all
diff --git a/libc/arch-arm/syscalls/splice.S b/libc/arch-arm/syscalls/splice.S
new file mode 100644
index 0000000..782ba6c
--- /dev/null
+++ b/libc/arch-arm/syscalls/splice.S
@@ -0,0 +1,22 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(splice)
+    mov     ip, sp
+    stmfd   sp!, {r4, r5, r6, r7}
+    .cfi_def_cfa_offset 16
+    .cfi_rel_offset r4, 0
+    .cfi_rel_offset r5, 4
+    .cfi_rel_offset r6, 8
+    .cfi_rel_offset r7, 12
+    ldmfd   ip, {r4, r5, r6}
+    ldr     r7, =__NR_splice
+    swi     #0
+    ldmfd   sp!, {r4, r5, r6, r7}
+    .cfi_def_cfa_offset 0
+    cmn     r0, #(MAX_ERRNO + 1)
+    bxls    lr
+    neg     r0, r0
+    b       __set_errno
+END(splice)
diff --git a/libc/arch-arm/syscalls/tee.S b/libc/arch-arm/syscalls/tee.S
new file mode 100644
index 0000000..9174617
--- /dev/null
+++ b/libc/arch-arm/syscalls/tee.S
@@ -0,0 +1,14 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(tee)
+    mov     ip, r7
+    ldr     r7, =__NR_tee
+    swi     #0
+    mov     r7, ip
+    cmn     r0, #(MAX_ERRNO + 1)
+    bxls    lr
+    neg     r0, r0
+    b       __set_errno
+END(tee)
diff --git a/libc/arch-arm/syscalls/vmsplice.S b/libc/arch-arm/syscalls/vmsplice.S
new file mode 100644
index 0000000..3b89623
--- /dev/null
+++ b/libc/arch-arm/syscalls/vmsplice.S
@@ -0,0 +1,14 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(vmsplice)
+    mov     ip, r7
+    ldr     r7, =__NR_vmsplice
+    swi     #0
+    mov     r7, ip
+    cmn     r0, #(MAX_ERRNO + 1)
+    bxls    lr
+    neg     r0, r0
+    b       __set_errno
+END(vmsplice)
diff --git a/libc/arch-arm64/syscalls/splice.S b/libc/arch-arm64/syscalls/splice.S
new file mode 100644
index 0000000..103805a
--- /dev/null
+++ b/libc/arch-arm64/syscalls/splice.S
@@ -0,0 +1,14 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(splice)
+    mov     x8, __NR_splice
+    svc     #0
+
+    cmn     x0, #(MAX_ERRNO + 1)
+    cneg    x0, x0, hi
+    b.hi    __set_errno
+
+    ret
+END(splice)
diff --git a/libc/arch-arm64/syscalls/tee.S b/libc/arch-arm64/syscalls/tee.S
new file mode 100644
index 0000000..d730076
--- /dev/null
+++ b/libc/arch-arm64/syscalls/tee.S
@@ -0,0 +1,14 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(tee)
+    mov     x8, __NR_tee
+    svc     #0
+
+    cmn     x0, #(MAX_ERRNO + 1)
+    cneg    x0, x0, hi
+    b.hi    __set_errno
+
+    ret
+END(tee)
diff --git a/libc/arch-arm64/syscalls/vmsplice.S b/libc/arch-arm64/syscalls/vmsplice.S
new file mode 100644
index 0000000..b4bec3f
--- /dev/null
+++ b/libc/arch-arm64/syscalls/vmsplice.S
@@ -0,0 +1,14 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(vmsplice)
+    mov     x8, __NR_vmsplice
+    svc     #0
+
+    cmn     x0, #(MAX_ERRNO + 1)
+    cneg    x0, x0, hi
+    b.hi    __set_errno
+
+    ret
+END(vmsplice)
diff --git a/libc/arch-mips/syscalls/splice.S b/libc/arch-mips/syscalls/splice.S
new file mode 100644
index 0000000..d344a6c
--- /dev/null
+++ b/libc/arch-mips/syscalls/splice.S
@@ -0,0 +1,19 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(splice)
+    .set noreorder
+    .cpload t9
+    li v0, __NR_splice
+    syscall
+    bnez a3, 1f
+    move a0, v0
+    j ra
+    nop
+1:
+    la t9,__set_errno
+    j t9
+    nop
+    .set reorder
+END(splice)
diff --git a/libc/arch-mips/syscalls/tee.S b/libc/arch-mips/syscalls/tee.S
new file mode 100644
index 0000000..e51732d
--- /dev/null
+++ b/libc/arch-mips/syscalls/tee.S
@@ -0,0 +1,19 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(tee)
+    .set noreorder
+    .cpload t9
+    li v0, __NR_tee
+    syscall
+    bnez a3, 1f
+    move a0, v0
+    j ra
+    nop
+1:
+    la t9,__set_errno
+    j t9
+    nop
+    .set reorder
+END(tee)
diff --git a/libc/arch-mips/syscalls/vmsplice.S b/libc/arch-mips/syscalls/vmsplice.S
new file mode 100644
index 0000000..24da515
--- /dev/null
+++ b/libc/arch-mips/syscalls/vmsplice.S
@@ -0,0 +1,19 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(vmsplice)
+    .set noreorder
+    .cpload t9
+    li v0, __NR_vmsplice
+    syscall
+    bnez a3, 1f
+    move a0, v0
+    j ra
+    nop
+1:
+    la t9,__set_errno
+    j t9
+    nop
+    .set reorder
+END(vmsplice)
diff --git a/libc/arch-mips64/syscalls/splice.S b/libc/arch-mips64/syscalls/splice.S
new file mode 100644
index 0000000..d626904
--- /dev/null
+++ b/libc/arch-mips64/syscalls/splice.S
@@ -0,0 +1,25 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(splice)
+    .set push
+    .set noreorder
+    li v0, __NR_splice
+    syscall
+    bnez a3, 1f
+    move a0, v0
+    j ra
+    nop
+1:
+    move t0, ra
+    bal     2f
+    nop
+2:
+    .cpsetup ra, t1, 2b
+    LA t9,__set_errno
+    .cpreturn
+    j t9
+    move ra, t0
+    .set pop
+END(splice)
diff --git a/libc/arch-mips64/syscalls/tee.S b/libc/arch-mips64/syscalls/tee.S
new file mode 100644
index 0000000..429700c
--- /dev/null
+++ b/libc/arch-mips64/syscalls/tee.S
@@ -0,0 +1,25 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(tee)
+    .set push
+    .set noreorder
+    li v0, __NR_tee
+    syscall
+    bnez a3, 1f
+    move a0, v0
+    j ra
+    nop
+1:
+    move t0, ra
+    bal     2f
+    nop
+2:
+    .cpsetup ra, t1, 2b
+    LA t9,__set_errno
+    .cpreturn
+    j t9
+    move ra, t0
+    .set pop
+END(tee)
diff --git a/libc/arch-mips64/syscalls/vmsplice.S b/libc/arch-mips64/syscalls/vmsplice.S
new file mode 100644
index 0000000..aa03585
--- /dev/null
+++ b/libc/arch-mips64/syscalls/vmsplice.S
@@ -0,0 +1,25 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(vmsplice)
+    .set push
+    .set noreorder
+    li v0, __NR_vmsplice
+    syscall
+    bnez a3, 1f
+    move a0, v0
+    j ra
+    nop
+1:
+    move t0, ra
+    bal     2f
+    nop
+2:
+    .cpsetup ra, t1, 2b
+    LA t9,__set_errno
+    .cpreturn
+    j t9
+    move ra, t0
+    .set pop
+END(vmsplice)
diff --git a/libc/arch-x86/syscalls/splice.S b/libc/arch-x86/syscalls/splice.S
new file mode 100644
index 0000000..46e2312
--- /dev/null
+++ b/libc/arch-x86/syscalls/splice.S
@@ -0,0 +1,46 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(splice)
+    pushl   %ebx
+    .cfi_def_cfa_offset 8
+    .cfi_rel_offset ebx, 0
+    pushl   %ecx
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset ecx, 0
+    pushl   %edx
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset edx, 0
+    pushl   %esi
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset esi, 0
+    pushl   %edi
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset edi, 0
+    pushl   %ebp
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset ebp, 0
+    mov     28(%esp), %ebx
+    mov     32(%esp), %ecx
+    mov     36(%esp), %edx
+    mov     40(%esp), %esi
+    mov     44(%esp), %edi
+    mov     48(%esp), %ebp
+    movl    $__NR_splice, %eax
+    int     $0x80
+    cmpl    $-MAX_ERRNO, %eax
+    jb      1f
+    negl    %eax
+    pushl   %eax
+    call    __set_errno
+    addl    $4, %esp
+1:
+    popl    %ebp
+    popl    %edi
+    popl    %esi
+    popl    %edx
+    popl    %ecx
+    popl    %ebx
+    ret
+END(splice)
diff --git a/libc/arch-x86/syscalls/tee.S b/libc/arch-x86/syscalls/tee.S
new file mode 100644
index 0000000..9422660
--- /dev/null
+++ b/libc/arch-x86/syscalls/tee.S
@@ -0,0 +1,36 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(tee)
+    pushl   %ebx
+    .cfi_def_cfa_offset 8
+    .cfi_rel_offset ebx, 0
+    pushl   %ecx
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset ecx, 0
+    pushl   %edx
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset edx, 0
+    pushl   %esi
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset esi, 0
+    mov     20(%esp), %ebx
+    mov     24(%esp), %ecx
+    mov     28(%esp), %edx
+    mov     32(%esp), %esi
+    movl    $__NR_tee, %eax
+    int     $0x80
+    cmpl    $-MAX_ERRNO, %eax
+    jb      1f
+    negl    %eax
+    pushl   %eax
+    call    __set_errno
+    addl    $4, %esp
+1:
+    popl    %esi
+    popl    %edx
+    popl    %ecx
+    popl    %ebx
+    ret
+END(tee)
diff --git a/libc/arch-x86/syscalls/vmsplice.S b/libc/arch-x86/syscalls/vmsplice.S
new file mode 100644
index 0000000..2afba60
--- /dev/null
+++ b/libc/arch-x86/syscalls/vmsplice.S
@@ -0,0 +1,36 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(vmsplice)
+    pushl   %ebx
+    .cfi_def_cfa_offset 8
+    .cfi_rel_offset ebx, 0
+    pushl   %ecx
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset ecx, 0
+    pushl   %edx
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset edx, 0
+    pushl   %esi
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset esi, 0
+    mov     20(%esp), %ebx
+    mov     24(%esp), %ecx
+    mov     28(%esp), %edx
+    mov     32(%esp), %esi
+    movl    $__NR_vmsplice, %eax
+    int     $0x80
+    cmpl    $-MAX_ERRNO, %eax
+    jb      1f
+    negl    %eax
+    pushl   %eax
+    call    __set_errno
+    addl    $4, %esp
+1:
+    popl    %esi
+    popl    %edx
+    popl    %ecx
+    popl    %ebx
+    ret
+END(vmsplice)
diff --git a/libc/arch-x86_64/syscalls/splice.S b/libc/arch-x86_64/syscalls/splice.S
new file mode 100644
index 0000000..3c245a5
--- /dev/null
+++ b/libc/arch-x86_64/syscalls/splice.S
@@ -0,0 +1,16 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(splice)
+    movq    %rcx, %r10
+    movl    $__NR_splice, %eax
+    syscall
+    cmpq    $-MAX_ERRNO, %rax
+    jb      1f
+    negl    %eax
+    movl    %eax, %edi
+    call    __set_errno
+1:
+    ret
+END(splice)
diff --git a/libc/arch-x86_64/syscalls/tee.S b/libc/arch-x86_64/syscalls/tee.S
new file mode 100644
index 0000000..ad5698c
--- /dev/null
+++ b/libc/arch-x86_64/syscalls/tee.S
@@ -0,0 +1,16 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(tee)
+    movq    %rcx, %r10
+    movl    $__NR_tee, %eax
+    syscall
+    cmpq    $-MAX_ERRNO, %rax
+    jb      1f
+    negl    %eax
+    movl    %eax, %edi
+    call    __set_errno
+1:
+    ret
+END(tee)
diff --git a/libc/arch-x86_64/syscalls/vmsplice.S b/libc/arch-x86_64/syscalls/vmsplice.S
new file mode 100644
index 0000000..cc94cc6
--- /dev/null
+++ b/libc/arch-x86_64/syscalls/vmsplice.S
@@ -0,0 +1,16 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(vmsplice)
+    movq    %rcx, %r10
+    movl    $__NR_vmsplice, %eax
+    syscall
+    cmpq    $-MAX_ERRNO, %rax
+    jb      1f
+    negl    %eax
+    movl    %eax, %edi
+    call    __set_errno
+1:
+    ret
+END(vmsplice)
diff --git a/libc/include/fcntl.h b/libc/include/fcntl.h
index cd68154..4450bb6 100644
--- a/libc/include/fcntl.h
+++ b/libc/include/fcntl.h
@@ -33,6 +33,7 @@
 #include <sys/types.h>
 #include <linux/fadvise.h>
 #include <linux/fcntl.h>
+#include <linux/uio.h>
 #include <unistd.h>  /* this is not required, but makes client code much happier */
 
 __BEGIN_DECLS
@@ -51,9 +52,12 @@
 #define F_SETLKW64 F_SETLKW
 #endif
 
-#ifndef O_ASYNC
-#define O_ASYNC  FASYNC
-#endif
+#define O_ASYNC FASYNC
+
+#define SPLICE_F_MOVE 1
+#define SPLICE_F_NONBLOCK 2
+#define SPLICE_F_MORE 4
+#define SPLICE_F_GIFT 8
 
 #define SYNC_FILE_RANGE_WAIT_BEFORE 1
 #define SYNC_FILE_RANGE_WRITE 2
@@ -70,7 +74,10 @@
 extern int open64(const char*, int, ...);
 extern int posix_fallocate64(int, off64_t, off64_t);
 extern int posix_fallocate(int, off_t, off_t);
+extern ssize_t splice(int, off64_t*, int, off64_t*, size_t, unsigned int);
+extern ssize_t tee(int, int, size_t, unsigned int);
 extern int unlinkat(int, const char*, int);
+extern ssize_t vmsplice(int, const struct iovec*, size_t, unsigned int);
 
 #if defined(__BIONIC_FORTIFY)
 
diff --git a/tests/fcntl_test.cpp b/tests/fcntl_test.cpp
index fb6b07e..5f20295 100644
--- a/tests/fcntl_test.cpp
+++ b/tests/fcntl_test.cpp
@@ -132,3 +132,87 @@
 
   close(fd);
 }
+
+TEST(fcntl, splice) {
+  int pipe_fds[2];
+  ASSERT_EQ(0, pipe(pipe_fds));
+
+  int in = open("/proc/cpuinfo", O_RDONLY);
+  ASSERT_NE(in, -1);
+
+  TemporaryFile tf;
+
+  ssize_t bytes_read = splice(in, 0, pipe_fds[1], NULL, 8*1024, SPLICE_F_MORE | SPLICE_F_MOVE);
+  ASSERT_NE(bytes_read, -1);
+
+  ssize_t bytes_written = splice(pipe_fds[0], NULL, tf.fd, 0, bytes_read, SPLICE_F_MORE | SPLICE_F_MOVE);
+  ASSERT_EQ(bytes_read, bytes_written);
+
+  close(pipe_fds[0]);
+  close(pipe_fds[1]);
+  close(in);
+}
+
+TEST(fcntl, vmsplice) {
+  int pipe_fds[2];
+  ASSERT_EQ(0, pipe(pipe_fds));
+
+  iovec v[2];
+  v[0].iov_base = const_cast<char*>("hello ");
+  v[0].iov_len = 6;
+  v[1].iov_base = const_cast<char*>("world\n");
+  v[1].iov_len = 6;
+  ssize_t bytes_written = vmsplice(pipe_fds[1], v, sizeof(v)/sizeof(iovec), 0);
+  ASSERT_EQ(v[0].iov_len + v[1].iov_len, static_cast<size_t>(bytes_written));
+  close(pipe_fds[1]);
+
+  char buf[BUFSIZ];
+  FILE* fp = fdopen(pipe_fds[0], "r");
+  ASSERT_TRUE(fp != NULL);
+  ASSERT_TRUE(fgets(buf, sizeof(buf), fp) != NULL);
+  fclose(fp);
+  ASSERT_STREQ("hello world\n", buf);
+}
+
+TEST(fcntl, tee) {
+  char expected[256];
+  FILE* expected_fp = fopen("/proc/version", "r");
+  ASSERT_TRUE(expected_fp != NULL);
+  ASSERT_TRUE(fgets(expected, sizeof(expected), expected_fp) != NULL);
+  fclose(expected_fp);
+
+  int pipe1[2];
+  ASSERT_EQ(0, pipe(pipe1));
+
+  int pipe2[2];
+  ASSERT_EQ(0, pipe(pipe2));
+
+  int in = open("/proc/version", O_RDONLY);
+  ASSERT_NE(in, -1);
+
+  // Write /proc/version into pipe1.
+  ssize_t bytes_read = splice(in, 0, pipe1[1], NULL, 8*1024, SPLICE_F_MORE | SPLICE_F_MOVE);
+  ASSERT_NE(bytes_read, -1);
+  close(pipe1[1]);
+
+  // Tee /proc/version from pipe1 into pipe2.
+  ssize_t bytes_teed = tee(pipe1[0], pipe2[1], SIZE_MAX, 0);
+  ASSERT_EQ(bytes_read, bytes_teed);
+  close(pipe2[1]);
+
+  // The out fds of both pipe1 and pipe2 should now contain /proc/version.
+  char buf1[BUFSIZ];
+  FILE* fp1 = fdopen(pipe1[0], "r");
+  ASSERT_TRUE(fp1 != NULL);
+  ASSERT_TRUE(fgets(buf1, sizeof(buf1), fp1) != NULL);
+  fclose(fp1);
+
+  char buf2[BUFSIZ];
+  FILE* fp2 = fdopen(pipe2[0], "r");
+  ASSERT_TRUE(fp2 != NULL);
+  ASSERT_TRUE(fgets(buf2, sizeof(buf2), fp2) != NULL);
+  fclose(fp2);
+
+  ASSERT_STREQ(expected, buf1);
+  ASSERT_STREQ(expected, buf2);
+}