From d64da095be4f2b78695426f7a921b61223ef7bd3 Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Mon, 1 Oct 2018 18:12:26 +0200 Subject: [PATCH 1/3] Implement binding for extended file attributes handling on Linux. --- .../src/com/sun/jna/platform/linux/XAttr.java | 86 +++ .../com/sun/jna/platform/linux/XAttrUtil.java | 626 ++++++++++++++++++ .../sun/jna/platform/linux/XAttrUtilTest.java | 70 ++ 3 files changed, 782 insertions(+) create mode 100644 contrib/platform/src/com/sun/jna/platform/linux/XAttr.java create mode 100644 contrib/platform/src/com/sun/jna/platform/linux/XAttrUtil.java create mode 100644 contrib/platform/test/com/sun/jna/platform/linux/XAttrUtilTest.java 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 From 62cd54e7dd17eafd45853703846b8b004d7d3cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Fri, 9 Nov 2018 21:55:41 +0100 Subject: [PATCH 2/3] Add missing CHANGES.md entry --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index b29cb34b2e..2328a39be7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ Features -------- * [#1029](https://github.com/java-native-access/jna/issues/1029): Add `statvfs` to `c.s.j.platform.linux.LibC` - [@dbwiddis](https://github.com/dbwiddis). * [#1032](https://github.com/java-native-access/jna/pull/1032): Deprecate `c.s.j.platform.win32.COM.util.annotation.ComEventCallback` in favour of `c.s.j.platform.win32.COM.util.annotation.ComMethod` - [@matthiasblaesing](https://github.com/matthiasblaesing). +* [#1021](https://github.com/java-native-access/jna/pull/1021): Added `com.sun.jna.platform.linux.XAttr` and `com.sun.jna.platform.linux.XAttrUtil` JNA wrapper for `` for Linux - [@wilx](https://github.com/wilx). Bug Fixes --------- From bd184d5b389666d7eeeda102c46914196e31da8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Fri, 9 Nov 2018 22:32:07 +0100 Subject: [PATCH 3/3] Add byte[] based bindings to c.s.j.p.linux.XAttr --- .../src/com/sun/jna/platform/linux/XAttr.java | 9 + .../com/sun/jna/platform/linux/XAttrUtil.java | 163 +++++++++++------- 2 files changed, 108 insertions(+), 64 deletions(-) diff --git a/contrib/platform/src/com/sun/jna/platform/linux/XAttr.java b/contrib/platform/src/com/sun/jna/platform/linux/XAttr.java index 8d36dae1b9..27337c5063 100644 --- a/contrib/platform/src/com/sun/jna/platform/linux/XAttr.java +++ b/contrib/platform/src/com/sun/jna/platform/linux/XAttr.java @@ -69,16 +69,25 @@ public ssize_t(long value) { int EDQUOT = 122; int setxattr(String path, String name, Pointer value, size_t size, int flags); + int setxattr(String path, String name, byte[] value, size_t size, int flags); int lsetxattr(String path, String name, Pointer value, size_t size, int flags); + int lsetxattr(String path, String name, byte[] value, size_t size, int flags); int fsetxattr(int fd, String name, Pointer value, size_t size, int flags); + int fsetxattr(int fd, String name, byte[] value, size_t size, int flags); ssize_t getxattr(String path, String name, Pointer value, size_t size); + ssize_t getxattr(String path, String name, byte[] value, size_t size); ssize_t lgetxattr(String path, String name, Pointer value, size_t size); + ssize_t lgetxattr(String path, String name, byte[] value, size_t size); ssize_t fgetxattr(int fd, String name, Pointer value, size_t size); + ssize_t fgetxattr(int fd, String name, byte[] value, size_t size); ssize_t listxattr(String path, Pointer list, size_t size); + ssize_t listxattr(String path, byte[] list, size_t size); ssize_t llistxattr(String path, Pointer list, size_t size); + ssize_t llistxattr(String path, byte[] list, size_t size); ssize_t flistxattr(int fd, Pointer list, size_t size); + ssize_t flistxattr(int fd, byte[] list, size_t size); int removexattr(String path, String name); int lremovexattr(String path, 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 index b5154ade3e..b9f7f75aeb 100644 --- a/contrib/platform/src/com/sun/jna/platform/linux/XAttrUtil.java +++ b/contrib/platform/src/com/sun/jna/platform/linux/XAttrUtil.java @@ -79,8 +79,7 @@ public static void setXAttr(String path, String name, String value, String encod * @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); + int retval = XAttr.INSTANCE.setxattr(path, name, value, new size_t(value.length), 0); if (retval != 0) { final int eno = Native.getLastError(); throw new IOException("errno: " + eno); @@ -126,9 +125,7 @@ public static void lSetXAttr(String path, String name, String value, String enco * @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); + final int retval = XAttr.INSTANCE.lsetxattr(path, name, value, new size_t(value.length), 0); if (retval != 0) { final int eno = Native.getLastError(); throw new IOException("errno: " + eno); @@ -171,9 +168,7 @@ public static void fSetXAttr(int fd, String name, String value, String encoding) * @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); + final int retval = XAttr.INSTANCE.fsetxattr(fd, name, value, new size_t(value.length), 0); if (retval != 0) { final int eno = Native.getLastError(); throw new IOException("errno: " + eno); @@ -203,10 +198,8 @@ public static String getXAttr(String path, String name) throws IOException { * @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(); + byte[] valueMem = getXAttrBytes(path, name); + return new String(valueMem, Charset.forName(encoding)); } /** @@ -218,8 +211,28 @@ public static String getXAttr(String path, String name, String encoding) throws * @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()); + ssize_t retval; + byte[] valueMem; + int eno = 0; + + do { + retval = XAttr.INSTANCE.getxattr(path, name, (byte[]) null, size_t.ZERO); + if (retval.longValue() < 0) { + eno = Native.getLastError(); + throw new IOException("errno: " + eno); + } + + valueMem = new byte[retval.intValue()]; + retval = XAttr.INSTANCE.getxattr(path, name, valueMem, new size_t(valueMem.length)); + 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; } /** @@ -236,7 +249,7 @@ public static Memory getXAttrAsMemory(String path, String name) throws IOExcepti int eno = 0; do { - retval = XAttr.INSTANCE.getxattr(path, name, null, size_t.ZERO); + retval = XAttr.INSTANCE.getxattr(path, name, (Memory) null, size_t.ZERO); if (retval.longValue() < 0) { eno = Native.getLastError(); throw new IOException("errno: " + eno); @@ -280,10 +293,8 @@ public static String lGetXAttr(String path, String name) throws IOException { * @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(); + byte[] valueMem = lGetXAttrBytes(path, name); + return new String(valueMem, Charset.forName(encoding)); } /** @@ -296,8 +307,28 @@ public static String lGetXAttr(String path, String name, String encoding) throws * @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()); + ssize_t retval; + byte[] valueMem; + int eno = 0; + + do { + retval = XAttr.INSTANCE.lgetxattr(path, name, (byte[]) null, size_t.ZERO); + if (retval.longValue() < 0) { + eno = Native.getLastError(); + throw new IOException("errno: " + eno); + } + + valueMem = new byte[retval.intValue()]; + retval = XAttr.INSTANCE.lgetxattr(path, name, valueMem, new size_t(valueMem.length)); + 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; } /** @@ -315,7 +346,7 @@ public static Memory lGetXAttrAsMemory(String path, String name) throws IOExcept int eno = 0; do { - retval = XAttr.INSTANCE.lgetxattr(path, name, null, size_t.ZERO); + retval = XAttr.INSTANCE.lgetxattr(path, name, (Memory) null, size_t.ZERO); if (retval.longValue() < 0) { eno = Native.getLastError(); throw new IOException("errno: " + eno); @@ -357,10 +388,8 @@ public static String fGetXAttr(int fd, String name) throws IOException { * @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(); + byte[] valueMem = fGetXAttrBytes(fd, name); + return new String(valueMem, Charset.forName(encoding)); } /** @@ -372,8 +401,28 @@ public static String fGetXAttr(int fd, String name, String encoding) throws IOEx * @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()); + ssize_t retval; + byte[] valueMem; + int eno = 0; + + do { + retval = XAttr.INSTANCE.fgetxattr(fd, name, (byte[]) null, size_t.ZERO); + if (retval.longValue() < 0) { + eno = Native.getLastError(); + throw new IOException("errno: " + eno); + } + + valueMem = new byte[retval.intValue()]; + retval = XAttr.INSTANCE.fgetxattr(fd, name, valueMem, new size_t(valueMem.length)); + 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; } /** @@ -390,7 +439,7 @@ public static Memory fGetXAttrAsMemory(int fd, String name) throws IOException { int eno = 0; do { - retval = XAttr.INSTANCE.fgetxattr(fd, name, null, size_t.ZERO); + retval = XAttr.INSTANCE.fgetxattr(fd, name, (Memory) null, size_t.ZERO); if (retval.longValue() < 0) { eno = Native.getLastError(); throw new IOException("errno: " + eno); @@ -431,18 +480,18 @@ public static Collection listXAttr(String path) throws IOException { */ public static Collection listXAttr(String path, String encoding) throws IOException { ssize_t retval; - Memory listMem; + byte[] listMem; int eno = 0; do { - retval = XAttr.INSTANCE.listxattr(path, null, size_t.ZERO); + retval = XAttr.INSTANCE.listxattr(path, (byte[]) 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())); + listMem = new byte[retval.intValue()]; + retval = XAttr.INSTANCE.listxattr(path, listMem, new size_t(listMem.length)); if (retval.longValue() < 0) { eno = Native.getLastError(); if (eno != XAttr.ERANGE) { @@ -478,18 +527,18 @@ public static Collection lListXAttr(String path) throws IOException { */ public static Collection lListXAttr(String path, String encoding) throws IOException { ssize_t retval; - Memory listMem; + byte[] listMem; int eno = 0; do { - retval = XAttr.INSTANCE.llistxattr(path, null, size_t.ZERO); + retval = XAttr.INSTANCE.llistxattr(path, (byte[]) 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())); + listMem = new byte[retval.intValue()]; + retval = XAttr.INSTANCE.llistxattr(path, listMem, new size_t(listMem.length)); if (retval.longValue() < 0) { eno = Native.getLastError(); if (eno != XAttr.ERANGE) { @@ -523,18 +572,18 @@ public static Collection fListXAttr(int fd) throws IOException { */ public static Collection fListXAttr(int fd, String encoding) throws IOException { ssize_t retval; - Memory listMem; + byte[] listMem; int eno = 0; do { - retval = XAttr.INSTANCE.flistxattr(fd, null, size_t.ZERO); + retval = XAttr.INSTANCE.flistxattr(fd, (byte[]) 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())); + listMem = new byte[retval.intValue()]; + retval = XAttr.INSTANCE.flistxattr(fd, listMem, new size_t(listMem.length)); if (retval.longValue() < 0) { eno = Native.getLastError(); if (eno != XAttr.ERANGE) { @@ -593,33 +642,19 @@ public static void fRemoveXAttr(int fd, String name) throws IOException { } } - 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) + private static Collection splitBufferToStrings(byte[] 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."); + int offset = 0; + for(int i = 0; i < valueMem.length; i++) { + // each entry is terminated by a single \0 byte + if(valueMem[i] == 0) { + // Convert bytes of the name to String. + final String name = new String(valueMem, offset, i - offset, charset); + attributesList.add(name); + offset = i + 1; } - - // 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; }