Add getxattr/setxattr/removexattr syscalls.

Bug: 20275572

(cherry picked from commit 82d076b51f6fe7c1cbd1f37414be36eaaa9b0e56)

Change-Id: Ifb808e69dddb88eba253ddf4273013828eab16f6
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
index fcecf18..a1e87c8 100644
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -215,6 +215,8 @@
    */
   public static int getuid() { return Libcore.os.getuid(); }
 
+  /** @hide */ public static int getxattr(String path, String name, byte[] outValue) throws ErrnoException { return Libcore.os.getxattr(path, name, outValue); }
+
   /**
    * See <a href="http://man7.org/linux/man-pages/man3/if_indextoname.3.html">if_indextoname(3)</a>.
    */
@@ -389,6 +391,8 @@
    */
   public static void remove(String path) throws ErrnoException { Libcore.os.remove(path); }
 
+  /** @hide */ public static void removexattr(String path, String name) throws ErrnoException { Libcore.os.removexattr(path, name); }
+
   /**
    * See <a href="http://man7.org/linux/man-pages/man2/rename.2.html">rename(2)</a>.
    */
@@ -468,6 +472,8 @@
    */
   public static void setuid(int uid) throws ErrnoException { Libcore.os.setuid(uid); }
 
+  /** @hide */ public static void setxattr(String path, String name, byte[] value, int flags) throws ErrnoException { Libcore.os.setxattr(path, name, value, flags); };
+
   /**
    * See <a href="http://man7.org/linux/man-pages/man2/shutdown.2.html">shutdown(2)</a>.
    */
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index 6a99bb3..777ad3b 100644
--- a/luni/src/main/java/android/system/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -496,6 +496,8 @@
     public static final int WSTOPPED = placeholder();
     public static final int WUNTRACED = placeholder();
     public static final int W_OK = placeholder();
+    /** @hide */ public static final int XATTR_CREATE = placeholder();
+    /** @hide */ public static final int XATTR_REPLACE = placeholder();
     public static final int X_OK = placeholder();
     public static final int _SC_2_CHAR_TERM = placeholder();
     public static final int _SC_2_C_BIND = placeholder();
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index 5c0c8fd..cb77573 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -96,6 +96,7 @@
     public StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptUcred(fd, level, option); }
     public int gettid() { return os.gettid(); }
     public int getuid() { return os.getuid(); }
+    public int getxattr(String path, String name, byte[] outValue) throws ErrnoException { return os.getxattr(path, name, outValue); }
     public String if_indextoname(int index) { return os.if_indextoname(index); }
     public InetAddress inet_pton(int family, String address) { return os.inet_pton(family, address); }
     public InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException { return os.ioctlInetAddress(fd, cmd, interfaceName); }
@@ -131,6 +132,7 @@
     public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, buffer, flags, srcAddress); }
     public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
     public void remove(String path) throws ErrnoException { os.remove(path); }
+    public void removexattr(String path, String name) throws ErrnoException { os.removexattr(path, name); }
     public void rename(String oldPath, String newPath) throws ErrnoException { os.rename(oldPath, newPath); }
     public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException { return os.sendfile(outFd, inFd, inOffset, byteCount); }
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, buffer, flags, inetAddress, port); }
@@ -153,6 +155,7 @@
     public void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException { os.setsockoptLinger(fd, level, option, value); }
     public void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException { os.setsockoptTimeval(fd, level, option, value); }
     public void setuid(int uid) throws ErrnoException { os.setuid(uid); }
+    public void setxattr(String path, String name, byte[] value, int flags) throws ErrnoException { os.setxattr(path, name, value, flags); }
     public void shutdown(FileDescriptor fd, int how) throws ErrnoException { os.shutdown(fd, how); }
     public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { return os.socket(domain, type, protocol); }
     public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException { os.socketpair(domain, type, protocol, fd1, fd2); }
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 987d331..6d28b95 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -88,6 +88,7 @@
     public StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException;
     public int gettid();
     public int getuid();
+    public int getxattr(String path, String name, byte[] outValue) throws ErrnoException;
     public String if_indextoname(int index);
     public InetAddress inet_pton(int family, String address);
     public InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException;
@@ -124,6 +125,7 @@
     public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
     public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
     public void remove(String path) throws ErrnoException;
+    public void removexattr(String path, String name) throws ErrnoException;
     public void rename(String oldPath, String newPath) throws ErrnoException;
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
     public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
@@ -146,6 +148,7 @@
     public void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException;
     public void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException;
     public void setuid(int uid) throws ErrnoException;
+    public void setxattr(String path, String name, byte[] value, int flags) throws ErrnoException;
     public void shutdown(FileDescriptor fd, int how) throws ErrnoException;
     public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException;
     public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException;
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index d680200..151809d 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -90,6 +90,7 @@
     public native StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException;
     public native int gettid();
     public native int getuid();
+    public native int getxattr(String path, String name, byte[] outValue) throws ErrnoException;
     public native String if_indextoname(int index);
     public native InetAddress inet_pton(int family, String address);
     public native InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException;
@@ -189,6 +190,7 @@
     }
     private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
     public native void remove(String path) throws ErrnoException;
+    public native void removexattr(String path, String name) throws ErrnoException;
     public native void rename(String oldPath, String newPath) throws ErrnoException;
     public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException;
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
@@ -230,6 +232,7 @@
     public native void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException;
     public native void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException;
     public native void setuid(int uid) throws ErrnoException;
+    public native void setxattr(String path, String name, byte[] value, int flags) throws ErrnoException;
     public native void shutdown(FileDescriptor fd, int how) throws ErrnoException;
     public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException;
     public native void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException;
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index a44d176..a11ea76 100644
--- a/luni/src/main/native/android_system_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -36,6 +36,7 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
+#include <sys/xattr.h>
 #include <unistd.h>
 
 #include <net/if_arp.h>
@@ -556,6 +557,8 @@
     initConstant(env, c, "WSTOPPED", WSTOPPED);
     initConstant(env, c, "WUNTRACED", WUNTRACED);
     initConstant(env, c, "W_OK", W_OK);
+    initConstant(env, c, "XATTR_CREATE", XATTR_CREATE);
+    initConstant(env, c, "XATTR_REPLACE", XATTR_REPLACE);
     initConstant(env, c, "X_OK", X_OK);
     initConstant(env, c, "_SC_2_CHAR_TERM", _SC_2_CHAR_TERM);
     initConstant(env, c, "_SC_2_C_BIND", _SC_2_C_BIND);
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index f6af483..99b76f9 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -59,6 +59,7 @@
 #include <sys/uio.h>
 #include <sys/utsname.h>
 #include <sys/wait.h>
+#include <sys/xattr.h>
 #include <termios.h>
 #include <unistd.h>
 #include <memory>
@@ -1061,6 +1062,28 @@
     return getuid();
 }
 
+static jint Posix_getxattr(JNIEnv* env, jobject, jstring javaPath,
+        jstring javaName, jbyteArray javaOutValue) {
+    ScopedUtfChars path(env, javaPath);
+    if (path.c_str() == NULL) {
+        return -1;
+    }
+    ScopedUtfChars name(env, javaName);
+    if (name.c_str() == NULL) {
+        return -1;
+    }
+    ScopedBytesRW outValue(env, javaOutValue);
+    if (outValue.get() == NULL) {
+        return -1;
+    }
+    size_t outValueLength = env->GetArrayLength(javaOutValue);
+    ssize_t size = getxattr(path.c_str(), name.c_str(), outValue.get(), outValueLength);
+    if (size < 0) {
+        throwErrnoException(env, "getxattr");
+    }
+    return size;
+}
+
 static jstring Posix_if_indextoname(JNIEnv* env, jobject, jint index) {
     char buf[IF_NAMESIZE];
     char* name = if_indextoname(index, buf);
@@ -1424,6 +1447,22 @@
     throwIfMinusOne(env, "remove", TEMP_FAILURE_RETRY(remove(path.c_str())));
 }
 
+static void Posix_removexattr(JNIEnv* env, jobject, jstring javaPath, jstring javaName) {
+    ScopedUtfChars path(env, javaPath);
+    if (path.c_str() == NULL) {
+        return;
+    }
+    ScopedUtfChars name(env, javaName);
+    if (name.c_str() == NULL) {
+        return;
+    }
+
+    int res = removexattr(path.c_str(), name.c_str());
+    if (res < 0) {
+        throwErrnoException(env, "removexattr");
+    }
+}
+
 static void Posix_rename(JNIEnv* env, jobject, jstring javaOldPath, jstring javaNewPath) {
     ScopedUtfChars oldPath(env, javaOldPath);
     if (oldPath.c_str() == NULL) {
@@ -1664,6 +1703,27 @@
     throwIfMinusOne(env, "setuid", TEMP_FAILURE_RETRY(setuid(uid)));
 }
 
+static void Posix_setxattr(JNIEnv* env, jobject, jstring javaPath, jstring javaName,
+        jbyteArray javaValue, jint flags) {
+    ScopedUtfChars path(env, javaPath);
+    if (path.c_str() == NULL) {
+        return;
+    }
+    ScopedUtfChars name(env, javaName);
+    if (name.c_str() == NULL) {
+        return;
+    }
+    ScopedBytesRO value(env, javaValue);
+    if (value.get() == NULL) {
+        return;
+    }
+    size_t valueLength = env->GetArrayLength(javaValue);
+    int res = setxattr(path.c_str(), name.c_str(), value.get(), valueLength, flags);
+    if (res < 0) {
+        throwErrnoException(env, "setxattr");
+    }
+}
+
 static void Posix_shutdown(JNIEnv* env, jobject, jobject javaFd, jint how) {
     int fd = jniGetFDFromFileDescriptor(env, javaFd);
     throwIfMinusOne(env, "shutdown", TEMP_FAILURE_RETRY(shutdown(fd, how)));
@@ -1842,6 +1902,7 @@
     NATIVE_METHOD(Posix, getsockoptUcred, "(Ljava/io/FileDescriptor;II)Landroid/system/StructUcred;"),
     NATIVE_METHOD(Posix, gettid, "()I"),
     NATIVE_METHOD(Posix, getuid, "()I"),
+    NATIVE_METHOD(Posix, getxattr, "(Ljava/lang/String;Ljava/lang/String;[B)I"),
     NATIVE_METHOD(Posix, if_indextoname, "(I)Ljava/lang/String;"),
     NATIVE_METHOD(Posix, inet_pton, "(ILjava/lang/String;)Ljava/net/InetAddress;"),
     NATIVE_METHOD(Posix, ioctlInetAddress, "(Ljava/io/FileDescriptor;ILjava/lang/String;)Ljava/net/InetAddress;"),
@@ -1873,6 +1934,7 @@
     NATIVE_METHOD(Posix, readv, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"),
     NATIVE_METHOD(Posix, recvfromBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetSocketAddress;)I"),
     NATIVE_METHOD(Posix, remove, "(Ljava/lang/String;)V"),
+    NATIVE_METHOD(Posix, removexattr, "(Ljava/lang/String;Ljava/lang/String;)V"),
     NATIVE_METHOD(Posix, rename, "(Ljava/lang/String;Ljava/lang/String;)V"),
     NATIVE_METHOD(Posix, sendfile, "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Landroid/util/MutableLong;J)J"),
     NATIVE_METHOD(Posix, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetAddress;I)I"),
@@ -1894,6 +1956,7 @@
     NATIVE_METHOD(Posix, setsockoptLinger, "(Ljava/io/FileDescriptor;IILandroid/system/StructLinger;)V"),
     NATIVE_METHOD(Posix, setsockoptTimeval, "(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V"),
     NATIVE_METHOD(Posix, setuid, "(I)V"),
+    NATIVE_METHOD(Posix, setxattr, "(Ljava/lang/String;Ljava/lang/String;[BI)V"),
     NATIVE_METHOD(Posix, shutdown, "(Ljava/io/FileDescriptor;I)V"),
     NATIVE_METHOD(Posix, socket, "(III)Ljava/io/FileDescriptor;"),
     NATIVE_METHOD(Posix, socketpair, "(IIILjava/io/FileDescriptor;Ljava/io/FileDescriptor;)V"),
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java
index e648e8a..9b38ee9 100644
--- a/luni/src/test/java/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/io/OsTest.java
@@ -18,6 +18,7 @@
 
 import android.system.ErrnoException;
 import android.system.NetlinkSocketAddress;
+import android.system.OsConstants;
 import android.system.PacketSocketAddress;
 import android.system.StructTimeval;
 import android.system.StructUcred;
@@ -419,4 +420,59 @@
     checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, true);
     checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false);
   }
+
+    private static void assertPartial(byte[] expected, byte[] actual) {
+        for (int i = 0; i < expected.length; i++) {
+            if (expected[i] != actual[i]) {
+                fail("Expected " + Arrays.toString(expected) + " but found "
+                        + Arrays.toString(actual));
+            }
+        }
+    }
+
+    public void test_xattr() throws Exception {
+        final String NAME_TEST = "user.meow";
+
+        final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
+        final byte[] VALUE_PIE = "pie".getBytes(StandardCharsets.UTF_8);
+
+        File file = File.createTempFile("xattr", "test");
+        String path = file.getAbsolutePath();
+
+        byte[] tmp = new byte[1024];
+        try {
+            try {
+                Libcore.os.getxattr(path, NAME_TEST, tmp);
+                fail("Expected ENODATA");
+            } catch (ErrnoException e) {
+                assertEquals(OsConstants.ENODATA, e.errno);
+            }
+
+            Libcore.os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
+            assertEquals(VALUE_CAKE.length, Libcore.os.getxattr(path, NAME_TEST, tmp));
+            assertPartial(VALUE_CAKE, tmp);
+
+            try {
+                Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_CREATE);
+                fail("Expected EEXIST");
+            } catch (ErrnoException e) {
+                assertEquals(OsConstants.EEXIST, e.errno);
+            }
+
+            Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_REPLACE);
+            assertEquals(VALUE_PIE.length, Libcore.os.getxattr(path, NAME_TEST, tmp));
+            assertPartial(VALUE_PIE, tmp);
+
+            Libcore.os.removexattr(path, NAME_TEST);
+            try {
+                Libcore.os.getxattr(path, NAME_TEST, tmp);
+                fail("Expected ENODATA");
+            } catch (ErrnoException e) {
+                assertEquals(OsConstants.ENODATA, e.errno);
+            }
+
+        } finally {
+            file.delete();
+        }
+    }
 }