diff --git a/contrib/platform/src/com/sun/jna/platform/linux/XAttr.java b/contrib/platform/src/com/sun/jna/platform/linux/XAttr.java
new file mode 100644
index 0000000000..8d36dae1b9
--- /dev/null
+++ b/contrib/platform/src/com/sun/jna/platform/linux/XAttr.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2018 Václav Haisman, All Rights Reserved
+ *
+ * The contents of this file is dual-licensed under 2
+ * alternative Open Source/Free licenses: LGPL 2.1 or later and
+ * Apache License 2.0.
+ *
+ * You can freely decide which license you want to apply to
+ * the project.
+ *
+ * You may obtain a copy of the LGPL License at:
+ *
+ * http://www.gnu.org/licenses/licenses.html
+ *
+ * A copy is also included in the downloadable source code package
+ * containing JNA, in file "LGPL2.1".
+ *
+ * You may obtain a copy of the Apache License at:
+ *
+ * http://www.apache.org/licenses/
+ *
+ * A copy is also included in the downloadable source code package
+ * containing JNA, in file "AL2.0".
+ */
+package com.sun.jna.platform.linux;
+
+import com.sun.jna.IntegerType;
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+
+public interface XAttr extends Library {
+ XAttr INSTANCE = Native.load(XAttr.class);
+
+ class size_t extends IntegerType {
+ public static final size_t ZERO = new size_t();
+
+ private static final long serialVersionUID = 1L;
+
+ public size_t() { this(0); }
+ public size_t(long value) { super(Native.SIZE_T_SIZE, value, true); }
+ }
+
+ class ssize_t extends IntegerType {
+ public static final ssize_t ZERO = new ssize_t();
+
+ private static final long serialVersionUID = 1L;
+
+ public ssize_t() {
+ this(0);
+ }
+
+ public ssize_t(long value) {
+ super(Native.SIZE_T_SIZE, value, false);
+ }
+ }
+
+ int XATTR_CREATE = 1;
+ int XATTR_REPLACE = 2;
+
+ int EPERM = 1;
+ int E2BIG = 7;
+ int EEXIST = 17;
+ int ENOSPC = 28;
+ int ERANGE = 34;
+ int ENODATA = 61;
+ int ENOATTR = ENODATA;
+ int ENOTSUP = 95;
+ int EDQUOT = 122;
+
+ int setxattr(String path, String name, Pointer value, size_t size, int flags);
+ int lsetxattr(String path, String name, Pointer value, size_t size, int flags);
+ int fsetxattr(int fd, String name, Pointer value, size_t size, int flags);
+
+ ssize_t getxattr(String path, String name, Pointer value, size_t size);
+ ssize_t lgetxattr(String path, String name, Pointer value, size_t size);
+ ssize_t fgetxattr(int fd, String name, Pointer value, size_t size);
+
+ ssize_t listxattr(String path, Pointer list, size_t size);
+ ssize_t llistxattr(String path, Pointer list, size_t size);
+ ssize_t flistxattr(int fd, Pointer list, size_t size);
+
+ int removexattr(String path, String name);
+ int lremovexattr(String path, String name);
+ int fremovexattr(int fd, String name);
+}
diff --git a/contrib/platform/src/com/sun/jna/platform/linux/XAttrUtil.java b/contrib/platform/src/com/sun/jna/platform/linux/XAttrUtil.java
new file mode 100644
index 0000000000..b5154ade3e
--- /dev/null
+++ b/contrib/platform/src/com/sun/jna/platform/linux/XAttrUtil.java
@@ -0,0 +1,626 @@
+/*
+ * Copyright (c) 2018 Václav Haisman, All Rights Reserved
+ *
+ * The contents of this file is dual-licensed under 2
+ * alternative Open Source/Free licenses: LGPL 2.1 or later and
+ * Apache License 2.0.
+ *
+ * You can freely decide which license you want to apply to
+ * the project.
+ *
+ * You may obtain a copy of the LGPL License at:
+ *
+ * http://www.gnu.org/licenses/licenses.html
+ *
+ * A copy is also included in the downloadable source code package
+ * containing JNA, in file "LGPL2.1".
+ *
+ * You may obtain a copy of the Apache License at:
+ *
+ * http://www.apache.org/licenses/
+ *
+ * A copy is also included in the downloadable source code package
+ * containing JNA, in file "AL2.0".
+ */
+package com.sun.jna.platform.linux;
+
+import com.sun.jna.Memory;
+import com.sun.jna.Native;
+import com.sun.jna.platform.linux.XAttr.size_t;
+import com.sun.jna.platform.linux.XAttr.ssize_t;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Utility functions class for handling file extended attributes on Linux.
+ */
+public abstract class XAttrUtil {
+
+ private XAttrUtil() {
+ }
+
+ /**
+ * Set or replace value of extended attribute.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @param value value to set
+ * @throws IOException on any error
+ */
+ public static void setXAttr(String path, String name, String value) throws IOException {
+ setXAttr(path, name, value, Native.getDefaultStringEncoding());
+ }
+
+ /**
+ * Set or replace value of extended attribute.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @param value value to set
+ * @param encoding character encoding to be used for stored value
+ * @throws IOException on any error
+ */
+ public static void setXAttr(String path, String name, String value, String encoding)
+ throws IOException {
+ setXAttr(path, name, value.getBytes(encoding));
+ }
+
+ /**
+ * Set or replace value of extended attribute.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @param value value to set
+ * @throws IOException on any error
+ */
+ public static void setXAttr(String path, String name, byte[] value) throws IOException {
+ Memory valueMem = bytesToMemory(value);
+ int retval = XAttr.INSTANCE.setxattr(path, name, valueMem, new size_t(valueMem.size()), 0);
+ if (retval != 0) {
+ final int eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+ }
+
+
+ /**
+ * Set or replace value of extended attribute but in case of symbolic link set the extended
+ * attribute on the link itself instead linked file.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @param value value to set
+ * @throws IOException on any error
+ */
+ public static void lSetXAttr(String path, String name, String value) throws IOException {
+ lSetXAttr(path, name, value, Native.getDefaultStringEncoding());
+ }
+
+ /**
+ * Set or replace value of extended attribute but in case of symbolic link set the extended
+ * attribute on the link itself instead linked file.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @param value value to set
+ * @param encoding character encoding to be used for stored value
+ * @throws IOException on any error
+ */
+ public static void lSetXAttr(String path, String name, String value, String encoding)
+ throws IOException {
+ lSetXAttr(path, name, value.getBytes(encoding));
+ }
+
+ /**
+ * Set or replace value of extended attribute but in case of symbolic link set the extended
+ * attribute on the link itself instead linked file.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @param value value to set
+ * @throws IOException on any error
+ */
+ public static void lSetXAttr(String path, String name, byte[] value) throws IOException {
+ Memory valueMem = bytesToMemory(value);
+ final int retval = XAttr.INSTANCE.lsetxattr(path, name, valueMem,
+ new size_t(valueMem.size()), 0);
+ if (retval != 0) {
+ final int eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+ }
+
+
+ /**
+ * Set or replace value of extended attribute.
+ *
+ * @param fd file handle
+ * @param name extended attribute name
+ * @param value value to set
+ * @throws IOException on any error
+ */
+ public static void fSetXAttr(int fd, String name, String value) throws IOException {
+ fSetXAttr(fd, name, value, Native.getDefaultStringEncoding());
+ }
+
+ /**
+ * Set or replace value of extended attribute.
+ *
+ * @param fd file handle
+ * @param name extended attribute name
+ * @param value value to set
+ * @param encoding character encoding to be used for stored value
+ * @throws IOException on any error
+ */
+ public static void fSetXAttr(int fd, String name, String value, String encoding)
+ throws IOException {
+ fSetXAttr(fd, name, value.getBytes(encoding));
+ }
+
+ /**
+ * Set or replace value of extended attribute.
+ *
+ * @param fd file handle
+ * @param name extended attribute name
+ * @param value value to set
+ * @throws IOException on any error
+ */
+ public static void fSetXAttr(int fd, String name, byte[] value) throws IOException {
+ Memory valueMem = bytesToMemory(value);
+ final int retval = XAttr.INSTANCE.fsetxattr(fd, name, valueMem, new size_t(valueMem.size()),
+ 0);
+ if (retval != 0) {
+ final int eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+ }
+
+
+ /**
+ * Get extended attribute value.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static String getXAttr(String path, String name) throws IOException {
+ return getXAttr(path, name, Native.getDefaultStringEncoding());
+ }
+
+ /**
+ * Get extended attribute value.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @param encoding character encoding to be used to decode stored extended attribute value
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static String getXAttr(String path, String name, String encoding) throws IOException {
+ Memory valueMem = getXAttrAsMemory(path, name);
+ return Charset.forName(encoding)
+ .decode(valueMem.getByteBuffer(0, valueMem.size()))
+ .toString();
+ }
+
+ /**
+ * Get extended attribute value.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static byte[] getXAttrBytes(String path, String name) throws IOException {
+ Memory valueMem = getXAttrAsMemory(path, name);
+ return valueMem.getByteArray(0, (int) valueMem.size());
+ }
+
+ /**
+ * Get extended attribute value.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static Memory getXAttrAsMemory(String path, String name) throws IOException {
+ ssize_t retval;
+ Memory valueMem;
+ int eno = 0;
+
+ do {
+ retval = XAttr.INSTANCE.getxattr(path, name, null, size_t.ZERO);
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+
+ valueMem = new Memory(retval.longValue());
+ retval = XAttr.INSTANCE.getxattr(path, name, valueMem, new size_t(valueMem.size()));
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ if (eno != XAttr.ERANGE) {
+ throw new IOException("errno: " + eno);
+ }
+ }
+ } while (retval.longValue() < 0 && eno == XAttr.ERANGE);
+
+ return valueMem;
+ }
+
+
+ /**
+ * Get extended attribute value but in case of symbolic link get the value from the link
+ * itself instead of linked file.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static String lGetXAttr(String path, String name) throws IOException {
+ return lGetXAttr(path, name, Native.getDefaultStringEncoding());
+ }
+
+ /**
+ * Get extended attribute value but in case of symbolic link get the value from the link
+ * itself instead of linked file.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @param encoding character encoding to be used to decode stored extended attribute value
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static String lGetXAttr(String path, String name, String encoding) throws IOException {
+ Memory valueMem = lGetXAttrAsMemory(path, name);
+ return Charset.forName(encoding)
+ .decode(valueMem.getByteBuffer(0, valueMem.size()))
+ .toString();
+ }
+
+ /**
+ * Get extended attribute value but in case of symbolic link get the value from the link
+ * itself instead of linked file.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static byte[] lGetXAttrBytes(String path, String name) throws IOException {
+ Memory valueMem = lGetXAttrAsMemory(path, name);
+ return valueMem.getByteArray(0, (int) valueMem.size());
+ }
+
+ /**
+ * Get extended attribute value but in case of symbolic link get the value from the link
+ * itself instead of linked file.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static Memory lGetXAttrAsMemory(String path, String name) throws IOException {
+ ssize_t retval;
+ Memory valueMem;
+ int eno = 0;
+
+ do {
+ retval = XAttr.INSTANCE.lgetxattr(path, name, null, size_t.ZERO);
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+
+ valueMem = new Memory(retval.longValue());
+ retval = XAttr.INSTANCE.lgetxattr(path, name, valueMem, new size_t(valueMem.size()));
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ if (eno != XAttr.ERANGE) {
+ throw new IOException("errno: " + eno);
+ }
+ }
+ } while (retval.longValue() < 0 && eno == XAttr.ERANGE);
+
+ return valueMem;
+ }
+
+
+ /**
+ * Get extended attribute value.
+ *
+ * @param fd file handle
+ * @param name extended attribute name
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static String fGetXAttr(int fd, String name) throws IOException {
+ return fGetXAttr(fd, name, Native.getDefaultStringEncoding());
+ }
+
+ /**
+ * Get extended attribute value.
+ *
+ * @param fd file handle
+ * @param name extended attribute name
+ * @param encoding character encoding to be used to decode stored extended attribute value
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static String fGetXAttr(int fd, String name, String encoding) throws IOException {
+ Memory valueMem = fGetXAttrAsMemory(fd, name);
+ return Charset.forName(encoding)
+ .decode(valueMem.getByteBuffer(0, valueMem.size()))
+ .toString();
+ }
+
+ /**
+ * Get extended attribute value.
+ *
+ * @param fd file handle
+ * @param name extended attribute name
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static byte[] fGetXAttrBytes(int fd, String name) throws IOException {
+ Memory valueMem = fGetXAttrAsMemory(fd, name);
+ return valueMem.getByteArray(0, (int) valueMem.size());
+ }
+
+ /**
+ * Get extended attribute value.
+ *
+ * @param fd file handle
+ * @param name extended attribute name
+ * @return extended attribute value
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static Memory fGetXAttrAsMemory(int fd, String name) throws IOException {
+ ssize_t retval;
+ Memory valueMem;
+ int eno = 0;
+
+ do {
+ retval = XAttr.INSTANCE.fgetxattr(fd, name, null, size_t.ZERO);
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+
+ valueMem = new Memory(retval.longValue());
+ retval = XAttr.INSTANCE.fgetxattr(fd, name, valueMem, new size_t(valueMem.size()));
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ if (eno != XAttr.ERANGE) {
+ throw new IOException("errno: " + eno);
+ }
+ }
+ } while (retval.longValue() < 0 && eno == XAttr.ERANGE);
+
+ return valueMem;
+ }
+
+
+ /**
+ * List extended attributes on file.
+ *
+ * @param path file path
+ * @return collection of extended attributes' names
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static Collection listXAttr(String path) throws IOException {
+ return listXAttr(path, Native.getDefaultStringEncoding());
+ }
+
+ /**
+ * List extended attributes on file.
+ *
+ * @param path file path
+ * @param encoding character encoding use to decode extended attributes' names
+ * @return collection of extended attributes' names
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static Collection listXAttr(String path, String encoding) throws IOException {
+ ssize_t retval;
+ Memory listMem;
+ int eno = 0;
+
+ do {
+ retval = XAttr.INSTANCE.listxattr(path, null, size_t.ZERO);
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+
+ listMem = new Memory(retval.longValue());
+ retval = XAttr.INSTANCE.listxattr(path, listMem, new size_t(listMem.size()));
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ if (eno != XAttr.ERANGE) {
+ throw new IOException("errno: " + eno);
+ }
+ }
+ } while (retval.longValue() < 0 && eno == XAttr.ERANGE);
+
+ return splitBufferToStrings(listMem, encoding);
+ }
+
+
+ /**
+ * List extended attributes on file but in case of symbolic link get extended attributes of
+ * the link itself instead of linked file.
+ *
+ * @param path file path
+ * @return collection of extended attributes' names
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static Collection lListXAttr(String path) throws IOException {
+ return lListXAttr(path, Native.getDefaultStringEncoding());
+ }
+
+ /**
+ * List extended attributes on file but in case of symbolic link get extended attributes of
+ * the link itself instead of linked file.
+ *
+ * @param path file path
+ * @param encoding character encoding use to decode extended attributes' names
+ * @return collection of extended attributes' names
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static Collection lListXAttr(String path, String encoding) throws IOException {
+ ssize_t retval;
+ Memory listMem;
+ int eno = 0;
+
+ do {
+ retval = XAttr.INSTANCE.llistxattr(path, null, size_t.ZERO);
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+
+ listMem = new Memory(retval.longValue());
+ retval = XAttr.INSTANCE.llistxattr(path, listMem, new size_t(listMem.size()));
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ if (eno != XAttr.ERANGE) {
+ throw new IOException("errno: " + eno);
+ }
+ }
+ } while (retval.longValue() < 0 && eno == XAttr.ERANGE);
+
+ return splitBufferToStrings(listMem, encoding);
+ }
+
+
+ /**
+ * List extended attributes on file.
+ *
+ * @param fd file handle
+ * @return collection of extended attributes' names
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static Collection fListXAttr(int fd) throws IOException {
+ return fListXAttr(fd, Native.getDefaultStringEncoding());
+ }
+
+ /**
+ * List extended attributes on file.
+ *
+ * @param fd file handle
+ * @param encoding character encoding use to decode extended attributes' names
+ * @return collection of extended attributes' names
+ * @throws IOException on any error except ERANGE
which handled internally
+ */
+ public static Collection fListXAttr(int fd, String encoding) throws IOException {
+ ssize_t retval;
+ Memory listMem;
+ int eno = 0;
+
+ do {
+ retval = XAttr.INSTANCE.flistxattr(fd, null, size_t.ZERO);
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+
+ listMem = new Memory(retval.longValue());
+ retval = XAttr.INSTANCE.flistxattr(fd, listMem, new size_t(listMem.size()));
+ if (retval.longValue() < 0) {
+ eno = Native.getLastError();
+ if (eno != XAttr.ERANGE) {
+ throw new IOException("errno: " + eno);
+ }
+ }
+ } while (retval.longValue() < 0 && eno == XAttr.ERANGE);
+
+ return splitBufferToStrings(listMem, encoding);
+ }
+
+
+ /**
+ * Remove extended attribute from file.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @throws IOException on any error
+ */
+ public static void removeXAttr(String path, String name) throws IOException {
+ final int retval = XAttr.INSTANCE.removexattr(path, name);
+ if (retval != 0) {
+ final int eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+ }
+
+ /**
+ * Remove extended attribute from file but in case of symbolic link remove extended attribute
+ * from the link itself instead of linked file.
+ *
+ * @param path file path
+ * @param name extended attribute name
+ * @throws IOException on any error
+ */
+ public static void lRemoveXAttr(String path, String name) throws IOException {
+ final int retval = XAttr.INSTANCE.lremovexattr(path, name);
+ if (retval != 0) {
+ final int eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+ }
+
+ /**
+ * Remove extended attribute from file.
+ *
+ * @param fd file handle
+ * @param name extended attribute name
+ * @throws IOException on any error
+ */
+ public static void fRemoveXAttr(int fd, String name) throws IOException {
+ final int retval = XAttr.INSTANCE.fremovexattr(fd, name);
+ if (retval != 0) {
+ final int eno = Native.getLastError();
+ throw new IOException("errno: " + eno);
+ }
+ }
+
+ private static Memory bytesToMemory(byte[] value) {
+ Memory valueMem = new Memory(value.length);
+ valueMem.write(0, value, 0, value.length);
+ return valueMem;
+ }
+
+ private static Collection splitBufferToStrings(Memory valueMem, String encoding)
+ throws IOException {
+ final Charset charset = Charset.forName(encoding);
+ final Set attributesList = new LinkedHashSet(1);
+ long offset = 0;
+ while (offset != valueMem.size()) {
+ // Find terminating NUL character.
+ long nulOffset = valueMem.indexOf(offset, (byte) 0);
+ if (nulOffset == -1) {
+ throw new IOException("Expected NUL character not found.");
+ }
+
+ // Duplicate buffer with limit at end of name.
+ final ByteBuffer nameBuffer = valueMem.getByteBuffer(offset, nulOffset);
+
+ // Convert bytes of the name to String.
+ final String name = charset.decode(nameBuffer).toString();
+ attributesList.add(name);
+
+ // Move past NUL.
+ offset += nulOffset + 1;
+ }
+ return attributesList;
+ }
+}
diff --git a/contrib/platform/test/com/sun/jna/platform/linux/XAttrUtilTest.java b/contrib/platform/test/com/sun/jna/platform/linux/XAttrUtilTest.java
new file mode 100644
index 0000000000..c4a074b6a5
--- /dev/null
+++ b/contrib/platform/test/com/sun/jna/platform/linux/XAttrUtilTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 Václav Haisman, All Rights Reserved
+ *
+ * The contents of this file is dual-licensed under 2
+ * alternative Open Source/Free licenses: LGPL 2.1 or later and
+ * Apache License 2.0.
+ *
+ * You can freely decide which license you want to apply to
+ * the project.
+ *
+ * You may obtain a copy of the LGPL License at:
+ *
+ * http://www.gnu.org/licenses/licenses.html
+ *
+ * A copy is also included in the downloadable source code package
+ * containing JNA, in file "LGPL2.1".
+ *
+ * You may obtain a copy of the Apache License at:
+ *
+ * http://www.apache.org/licenses/
+ *
+ * A copy is also included in the downloadable source code package
+ * containing JNA, in file "AL2.0".
+ */
+package com.sun.jna.platform.linux;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class XAttrUtilTest {
+ private static final String TEST_STRING = "Žluťoučký kůň úpěl nebo tak něco.";
+ private static final String TEST_STRING_2 = "Příliš žluťoučký kůň úpěl ďábelské ódy.";
+ private static final String TEST_ATTRIBUTE = "user.test";
+ private static final String TEST_ATTRIBUTE_FOO = TEST_ATTRIBUTE + ".foo";
+
+ @Test
+ public void setXAttr() throws IOException {
+ File file = File.createTempFile("xattr", "test");
+ file.deleteOnExit();
+
+ XAttrUtil.setXAttr(file.getAbsolutePath(), TEST_ATTRIBUTE, TEST_STRING);
+ XAttrUtil.setXAttr(file.getAbsolutePath(), TEST_ATTRIBUTE_FOO, TEST_STRING_2);
+
+ String retrievedValue = XAttrUtil.getXAttr(file.getAbsolutePath(), TEST_ATTRIBUTE);
+ assertEquals(TEST_STRING, retrievedValue);
+
+ retrievedValue = XAttrUtil.getXAttr(file.getAbsolutePath(), TEST_ATTRIBUTE_FOO);
+ assertEquals(TEST_STRING_2, retrievedValue);
+
+ XAttrUtil.setXAttr(file.getAbsolutePath(), TEST_ATTRIBUTE, TEST_STRING_2);
+ retrievedValue = XAttrUtil.lGetXAttr(file.getAbsolutePath(), TEST_ATTRIBUTE);
+ assertEquals(TEST_STRING_2, retrievedValue);
+
+ Collection xattrs = XAttrUtil.listXAttr(file.getAbsolutePath());
+ assertTrue(xattrs.contains(TEST_ATTRIBUTE));
+ assertTrue(xattrs.contains(TEST_ATTRIBUTE_FOO));
+
+ XAttrUtil.removeXAttr(file.getAbsolutePath(), TEST_ATTRIBUTE);
+ xattrs = XAttrUtil.lListXAttr(file.getAbsolutePath());
+ assertFalse(xattrs.contains(TEST_ATTRIBUTE));
+ assertTrue(xattrs.contains(TEST_ATTRIBUTE_FOO));
+ }
+}
\ No newline at end of file