Merge "wchar_test.cpp: fix error between comparison signed and unsigned integer"
diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp
index c9c5d28..79472b3 100644
--- a/libc/bionic/libc_logging.cpp
+++ b/libc/bionic/libc_logging.cpp
@@ -495,7 +495,7 @@
}
iovec vec[6];
- char log_id = LOG_ID_MAIN;
+ char log_id = (priority == ANDROID_LOG_FATAL) ? LOG_ID_CRASH : LOG_ID_MAIN;
vec[0].iov_base = &log_id;
vec[0].iov_len = sizeof(log_id);
uint16_t tid = gettid();
@@ -614,7 +614,7 @@
__libc_fatal("FORTIFY_SOURCE: %s. Calling abort().", msg);
}
-static void __libc_fatal(const char* tag, const char* format, va_list args) {
+static void __libc_fatal(const char* format, va_list args) {
char msg[1024];
BufferOutputStream os(msg, sizeof(msg));
out_vformat(os, format, args);
@@ -622,50 +622,53 @@
// TODO: log to stderr for the benefit of "adb shell" users.
// Log to the log for the benefit of regular app developers (whose stdout and stderr are closed).
- __libc_write_log(ANDROID_LOG_FATAL, tag, msg);
+ __libc_write_log(ANDROID_LOG_FATAL, "libc", msg);
- __libc_set_abort_message(msg);
+ __android_set_abort_message(msg);
}
void __libc_fatal_no_abort(const char* format, ...) {
va_list args;
va_start(args, format);
- __libc_fatal("libc", format, args);
+ __libc_fatal(format, args);
va_end(args);
}
void __libc_fatal(const char* format, ...) {
va_list args;
va_start(args, format);
- __libc_fatal("libc", format, args);
+ __libc_fatal(format, args);
va_end(args);
abort();
}
-// This is used by liblog to implement LOG_ALWAYS_FATAL and LOG_ALWAYS_FATAL_IF.
-void __android_fatal(const char* tag, const char* format, ...) {
- va_list args;
- va_start(args, format);
- __libc_fatal(tag, format, args);
- va_end(args);
- abort();
-}
+void __android_set_abort_message(const char* msg) {
+ ScopedPthreadMutexLocker locker(&gAbortMsgLock);
-void __libc_set_abort_message(const char* msg) {
+ if (__abort_message_ptr == NULL) {
+ // We must have crashed _very_ early.
+ return;
+ }
+
+ if (*__abort_message_ptr != NULL) {
+ // We already have an abort message.
+ // Assume that the first crash is the one most worth reporting.
+ return;
+ }
+
size_t size = sizeof(abort_msg_t) + strlen(msg) + 1;
void* map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
if (map == MAP_FAILED) {
return;
}
- if (__abort_message_ptr != NULL) {
- ScopedPthreadMutexLocker locker(&gAbortMsgLock);
- if (*__abort_message_ptr != NULL) {
- munmap(*__abort_message_ptr, (*__abort_message_ptr)->size);
- }
- abort_msg_t* new_abort_message = reinterpret_cast<abort_msg_t*>(map);
- new_abort_message->size = size;
- strcpy(new_abort_message->msg, msg);
- *__abort_message_ptr = new_abort_message;
+ // TODO: if we stick to the current "one-shot" scheme, we can remove this code and
+ // stop storing the size.
+ if (*__abort_message_ptr != NULL) {
+ munmap(*__abort_message_ptr, (*__abort_message_ptr)->size);
}
+ abort_msg_t* new_abort_message = reinterpret_cast<abort_msg_t*>(map);
+ new_abort_message->size = size;
+ strcpy(new_abort_message->msg, msg);
+ *__abort_message_ptr = new_abort_message;
}
diff --git a/libc/dns/gethnamaddr.c b/libc/dns/gethnamaddr.c
index dbcadee..4da99b2 100644
--- a/libc/dns/gethnamaddr.c
+++ b/libc/dns/gethnamaddr.c
@@ -828,27 +828,27 @@
assert(addr != NULL);
- if (af == AF_INET6 && len == IN6ADDRSZ &&
- (IN6_IS_ADDR_LINKLOCAL((const struct in6_addr *)(const void *)uaddr) ||
- IN6_IS_ADDR_SITELOCAL((const struct in6_addr *)(const void *)uaddr))) {
+ if (af == AF_INET6 && len == NS_IN6ADDRSZ &&
+ (IN6_IS_ADDR_LINKLOCAL((const struct in6_addr *)addr) ||
+ IN6_IS_ADDR_SITELOCAL((const struct in6_addr *)addr))) {
h_errno = HOST_NOT_FOUND;
return NULL;
}
- if (af == AF_INET6 && len == IN6ADDRSZ &&
- (IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)(const void *)uaddr) ||
- IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)(const void *)uaddr))) {
+ if (af == AF_INET6 && len == NS_IN6ADDRSZ &&
+ (IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)addr) ||
+ IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)addr))) {
/* Unmap. */
- addr += IN6ADDRSZ - INADDRSZ;
- uaddr += IN6ADDRSZ - INADDRSZ;
+ uaddr += NS_IN6ADDRSZ - NS_INADDRSZ;
+ addr = uaddr;
af = AF_INET;
- len = INADDRSZ;
+ len = NS_INADDRSZ;
}
switch (af) {
case AF_INET:
- size = INADDRSZ;
+ size = NS_INADDRSZ;
break;
case AF_INET6:
- size = IN6ADDRSZ;
+ size = NS_IN6ADDRSZ;
break;
default:
errno = EAFNOSUPPORT;
diff --git a/libc/include/android/api-level.h b/libc/include/android/api-level.h
index 611fdfb..2d2f096 100644
--- a/libc/include/android/api-level.h
+++ b/libc/include/android/api-level.h
@@ -25,9 +25,14 @@
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+
#ifndef ANDROID_API_LEVEL_H
#define ANDROID_API_LEVEL_H
-#define __ANDROID_API__ 10
+/*
+ * Magic version number for a current development build, which has
+ * not yet turned into an official release.
+ */
+#define __ANDROID_API__ 10000
#endif /* ANDROID_API_LEVEL_H */
diff --git a/libc/include/pthread.h b/libc/include/pthread.h
index 3f59b91..7b8de81 100644
--- a/libc/include/pthread.h
+++ b/libc/include/pthread.h
@@ -91,7 +91,7 @@
typedef long pthread_mutexattr_t;
typedef long pthread_condattr_t;
-typedef int pthread_rwlockattr_t;
+typedef long pthread_rwlockattr_t;
typedef struct {
pthread_mutex_t lock;
diff --git a/libc/include/semaphore.h b/libc/include/semaphore.h
index 30e3123..7ae3c3a 100644
--- a/libc/include/semaphore.h
+++ b/libc/include/semaphore.h
@@ -33,10 +33,13 @@
__BEGIN_DECLS
typedef struct {
- volatile unsigned int count;
+ volatile unsigned int count;
+#ifdef __LP64__
+ int __reserved[3];
+#endif
} sem_t;
-#define SEM_FAILED NULL
+#define SEM_FAILED NULL
extern int sem_init(sem_t *sem, int pshared, unsigned int value);
diff --git a/libc/include/sys/types.h b/libc/include/sys/types.h
index dc847d2..9a76ad2 100644
--- a/libc/include/sys/types.h
+++ b/libc/include/sys/types.h
@@ -63,7 +63,7 @@
typedef __kernel_key_t __key_t;
typedef __key_t key_t;
-typedef uint32_t __ino_t;
+typedef __kernel_ino_t __ino_t;
typedef __ino_t ino_t;
typedef uint32_t __nlink_t;
@@ -72,9 +72,10 @@
typedef void* __timer_t;
typedef __timer_t timer_t;
-typedef int32_t __suseconds_t;
+typedef __kernel_suseconds_t __suseconds_t;
typedef __suseconds_t suseconds_t;
+/* useconds_t is 32-bit on both LP32 and LP64. */
typedef uint32_t __useconds_t;
typedef __useconds_t useconds_t;
diff --git a/libc/include/utmp.h b/libc/include/utmp.h
index ffd3c92..d764227 100644
--- a/libc/include/utmp.h
+++ b/libc/include/utmp.h
@@ -36,9 +36,15 @@
#define _PATH_WTMP "/var/log/wtmp"
#define _PATH_LASTLOG "/var/log/lastlog"
-#define UT_NAMESIZE 8
-#define UT_LINESIZE 8
-#define UT_HOSTSIZE 16
+#ifdef __LP64__
+#define UT_NAMESIZE 32
+#define UT_LINESIZE 32
+#define UT_HOSTSIZE 256
+#else
+#define UT_NAMESIZE 8
+#define UT_LINESIZE 8
+#define UT_HOSTSIZE 16
+#endif
#define USER_PROCESS 7
diff --git a/libc/private/libc_logging.h b/libc/private/libc_logging.h
index 54e050a..7dd97a4 100644
--- a/libc/private/libc_logging.h
+++ b/libc/private/libc_logging.h
@@ -53,14 +53,15 @@
};
enum {
- LOG_ID_MIN = 0,
+ LOG_ID_MIN = 0,
- LOG_ID_MAIN = 0,
- LOG_ID_RADIO = 1,
- LOG_ID_EVENTS = 2,
- LOG_ID_SYSTEM = 3,
+ LOG_ID_MAIN = 0,
+ LOG_ID_RADIO = 1,
+ LOG_ID_EVENTS = 2,
+ LOG_ID_SYSTEM = 3,
+ LOG_ID_CRASH = 4,
- LOG_ID_MAX
+ LOG_ID_MAX
};
struct abort_msg_t {
@@ -68,14 +69,13 @@
char msg[0];
};
-__LIBC_HIDDEN__ void __libc_set_abort_message(const char* msg);
+void __android_set_abort_message(const char* msg);
//
// Formats a message to the log (priority 'fatal'), then aborts.
//
__LIBC_HIDDEN__ __noreturn void __libc_fatal(const char* format, ...) __printflike(1, 2);
-__noreturn void __android_fatal(const char* tag, const char* format, ...) __printflike(2, 3);
//
// Formats a message to the log (priority 'fatal'), but doesn't abort.
diff --git a/libc/upstream-openbsd/lib/libc/time/wcsftime.c b/libc/upstream-openbsd/lib/libc/time/wcsftime.c
index 5760493..21ccac7 100644
--- a/libc/upstream-openbsd/lib/libc/time/wcsftime.c
+++ b/libc/upstream-openbsd/lib/libc/time/wcsftime.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: wcsftime.c,v 1.2 2013/01/20 20:29:02 millert Exp $ */
+/* $OpenBSD: wcsftime.c,v 1.3 2014/05/06 15:49:45 tedu Exp $ */
#include "private.h"
/*
@@ -110,10 +110,6 @@
extern char * tzname[];
-#ifndef YEAR_2000_NAME
-#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
-#endif /* !defined YEAR_2000_NAME */
-
#define IN_NONE 0
#define IN_SOME 1
#define IN_THIS 2
@@ -129,22 +125,6 @@
tzset();
warn = IN_NONE;
p = _fmt(((format == NULL) ? L"%c" : format), t, s, s + maxsize, &warn);
-#ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
- if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
- (void) fprintf(stderr, "\n");
- if (format == NULL)
- (void) fprintf(stderr, "NULL strftime format ");
- else (void) fwprintf(stderr, "strftime format \"%ls\" ",
- format);
- (void) fprintf(stderr, "yields only two digits of years in ");
- if (warn == IN_SOME)
- (void) fprintf(stderr, "some locales");
- else if (warn == IN_THIS)
- (void) fprintf(stderr, "the current locale");
- else (void) fprintf(stderr, "all locales");
- (void) fprintf(stderr, "\n");
- }
-#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
if (p == s + maxsize) {
if (maxsize > 0)
s[maxsize - 1] = '\0';
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 08231ea..1f32f6d 100755
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -2071,6 +2071,12 @@
si->dynamic = NULL;
si->ref_count = 1;
+ ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(si->base);
+ if (elf_hdr->e_type != ET_DYN) {
+ __libc_format_fd(2, "error: only position independent executables (PIE) are supported.\n");
+ exit(EXIT_FAILURE);
+ }
+
// Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid).
parse_LD_LIBRARY_PATH(ldpath_env);
parse_LD_PRELOAD(ldpreload_env);
diff --git a/tests/Android.mk b/tests/Android.mk
index 9db372a..785a9f1 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -225,7 +225,7 @@
include $(LOCAL_PATH)/Android.build.mk
# -----------------------------------------------------------------------------
-# This library used by atexit tests
+# Library used by atexit tests
# -----------------------------------------------------------------------------
libtest_atexit_src_files := \
@@ -248,12 +248,19 @@
dlext_test.cpp \
dlfcn_test.cpp \
+bionic-unit-tests_cppflags := \
+ $(test_cppflags)
+
bionic-unit-tests_ldflags := \
-Wl,--export-dynamic \
-Wl,-u,DlSymTestFunction \
+bionic-unit-tests_c_includes := \
+ $(call include-path-for, libpagemap) \
+
bionic-unit-tests_shared_libraries_target := \
libdl \
+ libpagemap \
module := bionic-unit-tests
module_tag := optional
@@ -300,8 +307,12 @@
bionic-unit-tests-glibc_ldlibs := \
-lrt -ldl \
+bionic-unit-tests-glibc_cppflags := \
+ $(test_cppflags)
+
module := bionic-unit-tests-glibc
module_tag := optional
+bionic-unit-tests-glibc_multilib := both
build_type := host
build_target := NATIVE_TEST
include $(LOCAL_PATH)/Android.build.mk
diff --git a/tests/atexit_test.cpp b/tests/atexit_test.cpp
index e52235d..e01220e 100644
--- a/tests/atexit_test.cpp
+++ b/tests/atexit_test.cpp
@@ -24,7 +24,7 @@
#include <string>
-TEST(atexit, combined_test) {
+TEST(atexit, dlclose) {
std::string atexit_call_sequence;
bool valid_this_in_static_dtor = false;
void* handle = dlopen("libtest_atexit.so", RTLD_NOW);
@@ -40,5 +40,57 @@
ASSERT_TRUE(valid_this_in_static_dtor);
}
-// TODO: test for static dtor calls from from exit(.) -> __cxa_finalize(NULL)
+class TestMainStaticDtorClass {
+ public:
+ TestMainStaticDtorClass() {
+ expected_this = this;
+ }
+
+ ~TestMainStaticDtorClass() {
+ if (this != expected_this) {
+ fprintf(stderr, "\nerror: static d-tor called with incorrect this pointer: %p, expected: %p\n", this, expected_this);
+ } else {
+ fprintf(stderr, "6");
+ }
+ }
+ private:
+ static const TestMainStaticDtorClass* expected_this;
+};
+
+const TestMainStaticDtorClass* TestMainStaticDtorClass::expected_this = NULL;
+
+static void atexit_func5() {
+ fprintf(stderr, "5");
+}
+
+static void atexit_func4() {
+ fprintf(stderr, "4");
+}
+
+static void atexit_func3() {
+ fprintf(stderr, "3");
+ atexit(atexit_func4);
+}
+
+static void atexit_func2() {
+ fprintf(stderr, "2");
+}
+
+static void atexit_func1() {
+ fprintf(stderr, "1");
+}
+
+static void atexit_main() {
+ // This should result in "123456" output to stderr
+ static TestMainStaticDtorClass static_obj;
+ atexit(atexit_func5);
+ atexit(atexit_func3);
+ atexit(atexit_func2);
+ atexit(atexit_func1);
+ exit(0);
+}
+
+TEST(atexit, exit) {
+ ASSERT_EXIT(atexit_main(), testing::ExitedWithCode(0), "123456");
+}
diff --git a/tests/atexit_testlib.cpp b/tests/atexit_testlib.cpp
index 36393e7..1fefdf0 100644
--- a/tests/atexit_testlib.cpp
+++ b/tests/atexit_testlib.cpp
@@ -22,19 +22,20 @@
static std::string* atexit_sequence = NULL;
static bool* atexit_valid_this_in_static_dtor = NULL;
-class AtExitStaticClass;
-
-static const AtExitStaticClass* valid_this = NULL;
-
static class AtExitStaticClass {
-public:
- AtExitStaticClass() { valid_this = this; }
+ public:
+ AtExitStaticClass() { expected_this = this; }
~AtExitStaticClass() {
if (atexit_valid_this_in_static_dtor) {
- *atexit_valid_this_in_static_dtor = (valid_this == this);
+ *atexit_valid_this_in_static_dtor = (expected_this == this);
}
}
-} staticObj;
+ private:
+ static const AtExitStaticClass* expected_this;
+
+} static_obj;
+
+const AtExitStaticClass* AtExitStaticClass::expected_this = NULL;
// 4
static void atexit_handler_from_atexit_from_atexit2() {
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 4b2a5e2..b56fc41 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -24,8 +24,11 @@
#include <unistd.h>
#include <android/dlext.h>
#include <sys/mman.h>
+#include <sys/types.h>
#include <sys/wait.h>
+#include <pagemap/pagemap.h>
+
#define ASSERT_DL_NOTNULL(ptr) \
ASSERT_TRUE(ptr != NULL) << "dlerror: " << dlerror()
@@ -209,6 +212,8 @@
EXPECT_EQ(4, f());
}
+ void SpawnChildrenAndMeasurePss(const char* lib, bool share_relro, size_t* pss_out);
+
android_dlextinfo extinfo_;
char relro_file_[PATH_MAX];
};
@@ -230,3 +235,121 @@
ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME));
}
+
+TEST_F(DlExtRelroSharingTest, VerifyMemorySaving) {
+ ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME));
+ int relro_fd = open(relro_file_, O_RDONLY);
+ ASSERT_NOERROR(relro_fd);
+ extinfo_.flags |= ANDROID_DLEXT_USE_RELRO;
+ extinfo_.relro_fd = relro_fd;
+ int pipefd[2];
+ ASSERT_NOERROR(pipe(pipefd));
+
+ size_t without_sharing, with_sharing;
+ ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, false, &without_sharing));
+ ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, true, &with_sharing));
+
+ // We expect the sharing to save at least 10% of the total PSS. In practice
+ // it saves 40%+ for this test.
+ size_t expected_size = without_sharing - (without_sharing/10);
+ EXPECT_LT(with_sharing, expected_size);
+}
+
+void getPss(pid_t pid, size_t* pss_out) {
+ pm_kernel_t* kernel;
+ ASSERT_EQ(0, pm_kernel_create(&kernel));
+
+ pm_process_t* process;
+ ASSERT_EQ(0, pm_process_create(kernel, pid, &process));
+
+ pm_map_t** maps;
+ size_t num_maps;
+ ASSERT_EQ(0, pm_process_maps(process, &maps, &num_maps));
+
+ size_t total_pss = 0;
+ for (size_t i = 0; i < num_maps; i++) {
+ pm_memusage_t usage;
+ ASSERT_EQ(0, pm_map_usage(maps[i], &usage));
+ total_pss += usage.pss;
+ }
+ *pss_out = total_pss;
+
+ free(maps);
+ pm_process_destroy(process);
+ pm_kernel_destroy(kernel);
+}
+
+void DlExtRelroSharingTest::SpawnChildrenAndMeasurePss(const char* lib, bool share_relro,
+ size_t* pss_out) {
+ const int CHILDREN = 20;
+
+ // Create children
+ pid_t childpid[CHILDREN];
+ int childpipe[CHILDREN];
+ for (int i=0; i<CHILDREN; ++i) {
+ char read_buf;
+ int child_done_pipe[2], parent_done_pipe[2];
+ ASSERT_NOERROR(pipe(child_done_pipe));
+ ASSERT_NOERROR(pipe(parent_done_pipe));
+
+ pid_t child = fork();
+ if (child == 0) {
+ // close the 'wrong' ends of the pipes in the child
+ close(child_done_pipe[0]);
+ close(parent_done_pipe[1]);
+
+ // open the library
+ void* handle;
+ if (share_relro) {
+ handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_);
+ } else {
+ handle = dlopen(lib, RTLD_NOW);
+ }
+ if (handle == NULL) {
+ fprintf(stderr, "in child: %s\n", dlerror());
+ exit(1);
+ }
+
+ // close write end of child_done_pipe to signal the parent that we're done.
+ close(child_done_pipe[1]);
+
+ // wait for the parent to close parent_done_pipe, then exit
+ read(parent_done_pipe[0], &read_buf, 1);
+ exit(0);
+ }
+
+ ASSERT_NOERROR(child);
+
+ // close the 'wrong' ends of the pipes in the parent
+ close(child_done_pipe[1]);
+ close(parent_done_pipe[0]);
+
+ // wait for the child to be done
+ read(child_done_pipe[0], &read_buf, 1);
+ close(child_done_pipe[0]);
+
+ // save the child's pid and the parent_done_pipe
+ childpid[i] = child;
+ childpipe[i] = parent_done_pipe[1];
+ }
+
+ // Sum the PSS of all the children
+ size_t total_pss = 0;
+ for (int i=0; i<CHILDREN; ++i) {
+ size_t child_pss;
+ ASSERT_NO_FATAL_FAILURE(getPss(childpid[i], &child_pss));
+ total_pss += child_pss;
+ }
+ *pss_out = total_pss;
+
+ // Close pipes and wait for children to exit
+ for (int i=0; i<CHILDREN; ++i) {
+ ASSERT_NOERROR(close(childpipe[i]));
+ }
+ for (int i=0; i<CHILDREN; ++i) {
+ int status;
+ ASSERT_EQ(childpid[i], waitpid(childpid[i], &status, 0));
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(0, WEXITSTATUS(status));
+ }
+}
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index ce0beba..2b51aad 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -275,3 +275,46 @@
EXPECT_EQ(0, unsetenv("test-variable"));
}
+
+static void TestFsyncFunction(int (*fn)(int)) {
+ int fd;
+
+ // Can't sync an invalid fd.
+ errno = 0;
+ EXPECT_EQ(-1, fn(-1));
+ EXPECT_EQ(EBADF, errno);
+
+ // It doesn't matter whether you've opened a file for write or not.
+ TemporaryFile tf;
+ ASSERT_NE(-1, tf.fd);
+
+ EXPECT_EQ(0, fn(tf.fd));
+
+ ASSERT_NE(-1, fd = open(tf.filename, O_RDONLY));
+ EXPECT_EQ(0, fn(fd));
+ close(fd);
+
+ ASSERT_NE(-1, fd = open(tf.filename, O_RDWR));
+ EXPECT_EQ(0, fn(fd));
+ close(fd);
+
+ // The fd can even be a directory.
+ ASSERT_NE(-1, fd = open("/", O_RDONLY));
+ EXPECT_EQ(0, fn(fd));
+ close(fd);
+
+ // But some file systems may choose to be fussy...
+ errno = 0;
+ ASSERT_NE(-1, fd = open("/proc/version", O_RDONLY));
+ EXPECT_EQ(-1, fn(fd));
+ EXPECT_EQ(EINVAL, errno);
+ close(fd);
+}
+
+TEST(unistd, fdatasync) {
+ TestFsyncFunction(fdatasync);
+}
+
+TEST(unistd, fsync) {
+ TestFsyncFunction(fsync);
+}