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);
+}