Add futimes, futimesat, and lutimes.

Spotted these while cleaning up <sys/cdefs.h> --- if we remove __USE_XOPEN2K8,
libchrome decides you "must" have futimes. Adding the missing functions (all
just alternative interfaces to utimensat(2) system call) lets us clean up
without breaking anything.

Change-Id: If44fab08ee3de0e31066d650d128a3c96323529b
diff --git a/libc/Android.bp b/libc/Android.bp
index 4a44fc6..ba62592 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1373,6 +1373,7 @@
         "bionic/syslog.cpp",
         "bionic/sys_siglist.c",
         "bionic/sys_signame.c",
+        "bionic/sys_time.cpp",
         "bionic/system_properties.cpp",
         "bionic/tdestroy.cpp",
         "bionic/termios.cpp",
@@ -1380,7 +1381,6 @@
         "bionic/tmpfile.cpp",
         "bionic/umount.cpp",
         "bionic/unlink.cpp",
-        "bionic/utimes.cpp",
         "bionic/wait.cpp",
         "bionic/wchar.cpp",
         "bionic/wctype.cpp",
diff --git a/libc/bionic/utimes.cpp b/libc/bionic/sys_time.cpp
similarity index 67%
rename from libc/bionic/utimes.cpp
rename to libc/bionic/sys_time.cpp
index 0b66e6c..3d0cd87 100644
--- a/libc/bionic/utimes.cpp
+++ b/libc/bionic/sys_time.cpp
@@ -26,22 +26,40 @@
  * SUCH DAMAGE.
  */
 
+#include <sys/time.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <sys/time.h>
 
 #include "private/bionic_time_conversions.h"
 
-int utimes(const char* path, const timeval tv[2]) {
+static int futimesat(int fd, const char* path, const timeval tv[2], int flags) {
   timespec ts[2];
-  timespec* ts_ptr = NULL;
-  if (tv != NULL) {
-    if (!timespec_from_timeval(ts[0], tv[0]) || !timespec_from_timeval(ts[1], tv[1])) {
-      errno = EINVAL;
-      return -1;
-    }
-    ts_ptr = ts;
+  if (tv && (!timespec_from_timeval(ts[0], tv[0]) || !timespec_from_timeval(ts[1], tv[1]))) {
+    errno = EINVAL;
+    return -1;
   }
-  return utimensat(AT_FDCWD, path, ts_ptr, 0);
+  return utimensat(fd, path, tv ? ts : nullptr, flags);
+}
+
+int utimes(const char* path, const timeval tv[2]) {
+  return futimesat(AT_FDCWD, path, tv, 0);
+}
+
+int lutimes(const char* path, const timeval tv[2]) {
+  return futimesat(AT_FDCWD, path, tv, AT_SYMLINK_NOFOLLOW);
+}
+
+int futimesat(int fd, const char* path, const timeval tv[2]) {
+  return futimesat(fd, path, tv, 0);
+}
+
+int futimes(int fd, const timeval tv[2]) {
+  timespec ts[2];
+  if (tv && (!timespec_from_timeval(ts[0], tv[0]) || !timespec_from_timeval(ts[1], tv[1]))) {
+    errno = EINVAL;
+    return -1;
+  }
+  return futimens(fd, tv ? ts : nullptr);
 }
diff --git a/libc/include/sys/time.h b/libc/include/sys/time.h
index f60c905..f4ed321 100644
--- a/libc/include/sys/time.h
+++ b/libc/include/sys/time.h
@@ -38,13 +38,22 @@
 
 __BEGIN_DECLS
 
-extern int gettimeofday(struct timeval *, struct timezone *);
-extern int settimeofday(const struct timeval *, const struct timezone *);
+int gettimeofday(struct timeval*, struct timezone*);
+int settimeofday(const struct timeval*, const struct timezone*);
 
-extern int getitimer(int, struct itimerval *);
-extern int setitimer(int, const struct itimerval *, struct itimerval *);
+int getitimer(int, struct itimerval*);
+int setitimer(int, const struct itimerval*, struct itimerval*);
 
-extern int utimes(const char *, const struct timeval *);
+int utimes(const char*, const struct timeval*);
+
+#if defined(__USE_BSD)
+int futimes(int, const struct timeval[2]);
+int lutimes(const char*, const struct timeval[2]);
+#endif
+
+#if defined(__USE_GNU)
+int futimesat(int, const char*, const struct timeval[2]);
+#endif
 
 #define timerclear(a)   \
         ((a)->tv_sec = (a)->tv_usec = 0)
diff --git a/libc/libc.arm.brillo.map b/libc/libc.arm.brillo.map
index 7833d05..3928cae 100644
--- a/libc/libc.arm.brillo.map
+++ b/libc/libc.arm.brillo.map
@@ -1273,11 +1273,14 @@
     catopen;
     endgrent;
     endpwent;
+    futimes;
+    futimesat;
     getdomainname;
     getgrent;
     getpwent;
     getsubopt;
     hasmntopt;
+    lutimes;
     mblen;
     pthread_getname_np;
     quotactl;
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index a1b2511..f6520b5 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1273,11 +1273,14 @@
     catopen;
     endgrent;
     endpwent;
+    futimes;
+    futimesat;
     getdomainname;
     getgrent;
     getpwent;
     getsubopt;
     hasmntopt;
+    lutimes;
     mblen;
     pthread_getname_np;
     quotactl;
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index c853f73..603ac86 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1196,11 +1196,14 @@
     catopen;
     endgrent;
     endpwent;
+    futimes;
+    futimesat;
     getdomainname;
     getgrent;
     getpwent;
     getsubopt;
     hasmntopt;
+    lutimes;
     mblen;
     pthread_getname_np;
     quotactl;
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index d893b8b..d2ad249 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1298,11 +1298,14 @@
     catopen;
     endgrent;
     endpwent;
+    futimes;
+    futimesat;
     getdomainname;
     getgrent;
     getpwent;
     getsubopt;
     hasmntopt;
+    lutimes;
     mblen;
     pthread_getname_np;
     quotactl;
diff --git a/libc/libc.mips.brillo.map b/libc/libc.mips.brillo.map
index 56d4727..2cf5df0 100644
--- a/libc/libc.mips.brillo.map
+++ b/libc/libc.mips.brillo.map
@@ -1257,11 +1257,14 @@
     catopen;
     endgrent;
     endpwent;
+    futimes;
+    futimesat;
     getdomainname;
     getgrent;
     getpwent;
     getsubopt;
     hasmntopt;
+    lutimes;
     mblen;
     pthread_getname_np;
     quotactl;
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 35c56cb..6c5ba9c 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1257,11 +1257,14 @@
     catopen;
     endgrent;
     endpwent;
+    futimes;
+    futimesat;
     getdomainname;
     getgrent;
     getpwent;
     getsubopt;
     hasmntopt;
+    lutimes;
     mblen;
     pthread_getname_np;
     quotactl;
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index c853f73..603ac86 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1196,11 +1196,14 @@
     catopen;
     endgrent;
     endpwent;
+    futimes;
+    futimesat;
     getdomainname;
     getgrent;
     getpwent;
     getsubopt;
     hasmntopt;
+    lutimes;
     mblen;
     pthread_getname_np;
     quotactl;
diff --git a/libc/libc.x86.brillo.map b/libc/libc.x86.brillo.map
index a58d2bc..1a254ae 100644
--- a/libc/libc.x86.brillo.map
+++ b/libc/libc.x86.brillo.map
@@ -1255,11 +1255,14 @@
     catopen;
     endgrent;
     endpwent;
+    futimes;
+    futimesat;
     getdomainname;
     getgrent;
     getpwent;
     getsubopt;
     hasmntopt;
+    lutimes;
     mblen;
     pthread_getname_np;
     quotactl;
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 2fe730f..ca97c03 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1255,11 +1255,14 @@
     catopen;
     endgrent;
     endpwent;
+    futimes;
+    futimesat;
     getdomainname;
     getgrent;
     getpwent;
     getsubopt;
     hasmntopt;
+    lutimes;
     mblen;
     pthread_getname_np;
     quotactl;
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index c853f73..603ac86 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1196,11 +1196,14 @@
     catopen;
     endgrent;
     endpwent;
+    futimes;
+    futimesat;
     getdomainname;
     getgrent;
     getpwent;
     getsubopt;
     hasmntopt;
+    lutimes;
     mblen;
     pthread_getname_np;
     quotactl;
diff --git a/tests/sys_time_test.cpp b/tests/sys_time_test.cpp
index 18fbe10..e041ba0 100644
--- a/tests/sys_time_test.cpp
+++ b/tests/sys_time_test.cpp
@@ -22,30 +22,113 @@
 
 #include "TemporaryFile.h"
 
-TEST(sys_time, utimes) {
-  timeval tv[2];
-  memset(&tv, 0, sizeof(tv));
+// http://b/11383777
+TEST(sys_time, utimes_nullptr) {
+  TemporaryFile tf;
+  ASSERT_EQ(0, utimes(tf.filename, nullptr));
+}
+
+TEST(sys_time, utimes_EINVAL) {
+  TemporaryFile tf;
+
+  timeval tv[2] = {};
 
   tv[0].tv_usec = -123;
-  ASSERT_EQ(-1, utimes("/", tv));
+  ASSERT_EQ(-1, utimes(tf.filename, tv));
   ASSERT_EQ(EINVAL, errno);
   tv[0].tv_usec = 1234567;
-  ASSERT_EQ(-1, utimes("/", tv));
+  ASSERT_EQ(-1, utimes(tf.filename, tv));
   ASSERT_EQ(EINVAL, errno);
+
   tv[0].tv_usec = 0;
 
   tv[1].tv_usec = -123;
-  ASSERT_EQ(-1, utimes("/", tv));
+  ASSERT_EQ(-1, utimes(tf.filename, tv));
   ASSERT_EQ(EINVAL, errno);
   tv[1].tv_usec = 1234567;
-  ASSERT_EQ(-1, utimes("/", tv));
+  ASSERT_EQ(-1, utimes(tf.filename, tv));
   ASSERT_EQ(EINVAL, errno);
 }
 
-// http://b/11383777
-TEST(sys_time, utimes_NULL) {
+TEST(sys_time, futimes_nullptr) {
   TemporaryFile tf;
-  ASSERT_EQ(0, utimes(tf.filename, NULL));
+  ASSERT_EQ(0, futimes(tf.fd, nullptr));
+}
+
+TEST(sys_time, futimes_EINVAL) {
+  TemporaryFile tf;
+
+  timeval tv[2] = {};
+
+  tv[0].tv_usec = -123;
+  ASSERT_EQ(-1, futimes(tf.fd, tv));
+  ASSERT_EQ(EINVAL, errno);
+  tv[0].tv_usec = 1234567;
+  ASSERT_EQ(-1, futimes(tf.fd, tv));
+  ASSERT_EQ(EINVAL, errno);
+
+  tv[0].tv_usec = 0;
+
+  tv[1].tv_usec = -123;
+  ASSERT_EQ(-1, futimes(tf.fd, tv));
+  ASSERT_EQ(EINVAL, errno);
+  tv[1].tv_usec = 1234567;
+  ASSERT_EQ(-1, futimes(tf.fd, tv));
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(sys_time, futimesat_nullptr) {
+  TemporaryFile tf;
+  ASSERT_EQ(0, futimesat(AT_FDCWD, tf.filename, nullptr));
+}
+
+TEST(sys_time, futimesat_EINVAL) {
+  TemporaryFile tf;
+
+  timeval tv[2] = {};
+
+  tv[0].tv_usec = -123;
+  ASSERT_EQ(-1, futimesat(AT_FDCWD, tf.filename, tv));
+  ASSERT_EQ(EINVAL, errno);
+  tv[0].tv_usec = 1234567;
+  ASSERT_EQ(-1, futimesat(AT_FDCWD, tf.filename, tv));
+  ASSERT_EQ(EINVAL, errno);
+
+  tv[0].tv_usec = 0;
+
+  tv[1].tv_usec = -123;
+  ASSERT_EQ(-1, futimesat(AT_FDCWD, tf.filename, tv));
+  ASSERT_EQ(EINVAL, errno);
+  tv[1].tv_usec = 1234567;
+  ASSERT_EQ(-1, futimesat(AT_FDCWD, tf.filename, tv));
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(sys_time, lutimes_nullptr) {
+  TemporaryFile tf;
+  ASSERT_EQ(0, lutimes(tf.filename, nullptr));
+}
+
+TEST(sys_time, lutimes_EINVAL) {
+  TemporaryFile tf;
+
+  timeval tv[2] = {};
+
+  tv[0].tv_usec = -123;
+  ASSERT_EQ(-1, lutimes(tf.filename, tv));
+  ASSERT_EQ(EINVAL, errno);
+  tv[0].tv_usec = 1234567;
+  ASSERT_EQ(-1, lutimes(tf.filename, tv));
+  ASSERT_EQ(EINVAL, errno);
+
+  tv[0].tv_usec = 0;
+
+  tv[1].tv_usec = -123;
+  ASSERT_EQ(-1, lutimes(tf.filename, tv));
+  ASSERT_EQ(EINVAL, errno);
+  tv[1].tv_usec = 1234567;
+  ASSERT_EQ(-1, lutimes(tf.filename, tv));
+  ASSERT_EQ(EINVAL, errno);
 }
 
 TEST(sys_time, gettimeofday) {