Add sync_file_range to <fcntl.h>.

Bug: http://b/27952303
Change-Id: Idadfacd657ed415abc11684b9471e4e24c2fbf05
diff --git a/libc/Android.mk b/libc/Android.mk
index aed7087..0e4dc11 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -199,6 +199,7 @@
     bionic/strtold.cpp \
     bionic/stubs.cpp \
     bionic/symlink.cpp \
+    bionic/sync_file_range.cpp \
     bionic/sysinfo.cpp \
     bionic/syslog.cpp \
     bionic/sys_siglist.c \
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index b774dbc..87f600b 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -330,6 +330,9 @@
 int setdomainname(const char*, size_t)  all
 int sethostname(const char*, size_t)  all
 
+int __sync_file_range:sync_file_range(int, off64_t, off64_t, unsigned int) arm64,mips,mips64,x86,x86_64
+int __sync_file_range2:sync_file_range2(int, unsigned int, off64_t, off64_t) arm
+
 pid_t wait4(pid_t, int*, int, struct rusage*)  all
 int __waitid:waitid(int, pid_t, struct siginfo_t*, int, void*)  all
 
diff --git a/libc/arch-arm/syscalls/__sync_file_range2.S b/libc/arch-arm/syscalls/__sync_file_range2.S
new file mode 100644
index 0000000..4346e1b
--- /dev/null
+++ b/libc/arch-arm/syscalls/__sync_file_range2.S
@@ -0,0 +1,22 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__sync_file_range2)
+    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_sync_file_range2
+    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_internal
+END(__sync_file_range2)
diff --git a/libc/arch-arm64/syscalls/__sync_file_range.S b/libc/arch-arm64/syscalls/__sync_file_range.S
new file mode 100644
index 0000000..776e900
--- /dev/null
+++ b/libc/arch-arm64/syscalls/__sync_file_range.S
@@ -0,0 +1,15 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__sync_file_range)
+    mov     x8, __NR_sync_file_range
+    svc     #0
+
+    cmn     x0, #(MAX_ERRNO + 1)
+    cneg    x0, x0, hi
+    b.hi    __set_errno_internal
+
+    ret
+END(__sync_file_range)
+.hidden __sync_file_range
diff --git a/libc/arch-mips/syscalls/__sync_file_range.S b/libc/arch-mips/syscalls/__sync_file_range.S
new file mode 100644
index 0000000..79d41e0
--- /dev/null
+++ b/libc/arch-mips/syscalls/__sync_file_range.S
@@ -0,0 +1,19 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__sync_file_range)
+    .set noreorder
+    .cpload t9
+    li v0, __NR_sync_file_range
+    syscall
+    bnez a3, 1f
+    move a0, v0
+    j ra
+    nop
+1:
+    la t9,__set_errno_internal
+    j t9
+    nop
+    .set reorder
+END(__sync_file_range)
diff --git a/libc/arch-mips64/syscalls/__sync_file_range.S b/libc/arch-mips64/syscalls/__sync_file_range.S
new file mode 100644
index 0000000..e22c36e
--- /dev/null
+++ b/libc/arch-mips64/syscalls/__sync_file_range.S
@@ -0,0 +1,26 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__sync_file_range)
+    .set push
+    .set noreorder
+    li v0, __NR_sync_file_range
+    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_internal
+    .cpreturn
+    j t9
+    move ra, t0
+    .set pop
+END(__sync_file_range)
+.hidden __sync_file_range
diff --git a/libc/arch-x86/syscalls/__sync_file_range.S b/libc/arch-x86/syscalls/__sync_file_range.S
new file mode 100644
index 0000000..f5bf3ec
--- /dev/null
+++ b/libc/arch-x86/syscalls/__sync_file_range.S
@@ -0,0 +1,54 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__sync_file_range)
+    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
+
+    call    __kernel_syscall
+    pushl   %eax
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset eax, 0
+
+    mov     32(%esp), %ebx
+    mov     36(%esp), %ecx
+    mov     40(%esp), %edx
+    mov     44(%esp), %esi
+    mov     48(%esp), %edi
+    mov     52(%esp), %ebp
+    movl    $__NR_sync_file_range, %eax
+    call    *(%esp)
+    addl    $4, %esp
+
+    cmpl    $-MAX_ERRNO, %eax
+    jb      1f
+    negl    %eax
+    pushl   %eax
+    call    __set_errno_internal
+    addl    $4, %esp
+1:
+    popl    %ebp
+    popl    %edi
+    popl    %esi
+    popl    %edx
+    popl    %ecx
+    popl    %ebx
+    ret
+END(__sync_file_range)
diff --git a/libc/arch-x86_64/syscalls/__sync_file_range.S b/libc/arch-x86_64/syscalls/__sync_file_range.S
new file mode 100644
index 0000000..6a2c430
--- /dev/null
+++ b/libc/arch-x86_64/syscalls/__sync_file_range.S
@@ -0,0 +1,17 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__sync_file_range)
+    movq    %rcx, %r10
+    movl    $__NR_sync_file_range, %eax
+    syscall
+    cmpq    $-MAX_ERRNO, %rax
+    jb      1f
+    negl    %eax
+    movl    %eax, %edi
+    call    __set_errno_internal
+1:
+    ret
+END(__sync_file_range)
+.hidden __sync_file_range
diff --git a/libc/bionic/sync_file_range.cpp b/libc/bionic/sync_file_range.cpp
new file mode 100644
index 0000000..7f60882
--- /dev/null
+++ b/libc/bionic/sync_file_range.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <fcntl.h>
+
+extern "C" int __sync_file_range(int, off64_t, off64_t, unsigned int);
+extern "C" int __sync_file_range2(int, unsigned int, off64_t, off64_t);
+
+int sync_file_range(int fd, off64_t offset, off64_t length, unsigned int flags) {
+#if __arm__
+  return __sync_file_range2(fd, flags, offset, length);
+#else
+  return __sync_file_range(fd, offset, length, flags);
+#endif
+}
diff --git a/libc/include/fcntl.h b/libc/include/fcntl.h
index 74ea523..9d15e92 100644
--- a/libc/include/fcntl.h
+++ b/libc/include/fcntl.h
@@ -88,6 +88,7 @@
 
 #if defined(__USE_GNU)
 ssize_t readahead(int, off64_t, size_t);
+int sync_file_range(int, off64_t, off64_t, unsigned int);
 #endif
 
 extern int __open_2(const char*, int);
diff --git a/libc/libc.arm.brillo.map b/libc/libc.arm.brillo.map
index c249881..2d286af 100644
--- a/libc/libc.arm.brillo.map
+++ b/libc/libc.arm.brillo.map
@@ -1283,6 +1283,7 @@
     sigpause;
     sigrelse;
     sigset;
+    sync_file_range;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 9890b14..40f645c 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1283,6 +1283,7 @@
     sigpause;
     sigrelse;
     sigset;
+    sync_file_range;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index a649ca2..61bff70 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1205,6 +1205,7 @@
     sigpause;
     sigrelse;
     sigset;
+    sync_file_range;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 9f2002f..d293264 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1309,6 +1309,7 @@
     sigpause;
     sigrelse;
     sigset;
+    sync_file_range;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.mips.brillo.map b/libc/libc.mips.brillo.map
index a14ef42..5c10069 100644
--- a/libc/libc.mips.brillo.map
+++ b/libc/libc.mips.brillo.map
@@ -1267,6 +1267,7 @@
     sigpause;
     sigrelse;
     sigset;
+    sync_file_range;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index dec8352..baee8fb 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1267,6 +1267,7 @@
     sigpause;
     sigrelse;
     sigset;
+    sync_file_range;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index a649ca2..61bff70 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1205,6 +1205,7 @@
     sigpause;
     sigrelse;
     sigset;
+    sync_file_range;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.x86.brillo.map b/libc/libc.x86.brillo.map
index a186fb3..587cf74 100644
--- a/libc/libc.x86.brillo.map
+++ b/libc/libc.x86.brillo.map
@@ -1266,6 +1266,7 @@
     sigpause;
     sigrelse;
     sigset;
+    sync_file_range;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index d594a9a..9a38ee9 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1266,6 +1266,7 @@
     sigpause;
     sigrelse;
     sigset;
+    sync_file_range;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index a649ca2..61bff70 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1205,6 +1205,7 @@
     sigpause;
     sigrelse;
     sigset;
+    sync_file_range;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/tests/fcntl_test.cpp b/tests/fcntl_test.cpp
index 212cce6..0798686 100644
--- a/tests/fcntl_test.cpp
+++ b/tests/fcntl_test.cpp
@@ -244,3 +244,19 @@
   ASSERT_EQ(-1, readahead(-1, 0, 123));
   ASSERT_EQ(EBADF, errno);
 }
+
+TEST(fcntl, sync_file_range) {
+  // Just check that the function is available.
+  errno = 0;
+  ASSERT_EQ(-1, sync_file_range(-1, 0, 0, 0));
+  ASSERT_EQ(EBADF, errno);
+
+  TemporaryFile tf;
+  ASSERT_EQ(0, sync_file_range(tf.fd, 0, 0, 0));
+
+  // The arguments to the underlying system call are in a different order on 32-bit ARM.
+  // Check that the `flags` argument gets passed to the kernel correctly.
+  errno = 0;
+  ASSERT_EQ(-1, sync_file_range(tf.fd, 0, 0, ~0));
+  ASSERT_EQ(EINVAL, errno);
+}