Skip to content

Commit

Permalink
Merge pull request #338 from rednoah/master
Browse files Browse the repository at this point in the history
JNA wrapper for <sys/xattr.h>
  • Loading branch information
twall committed Jun 20, 2014
2 parents 296e89e + 21d7352 commit 33dcdb0
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Features
* Added Some minor changes to MS Office samples Test and small changes to the MS Office samples Bug Fixes - [@wolftobias](https://github.com/wolftobias).
* [#333](https://github.com/twall/jna/pull/333): Added `CoTaskMemAlloc`, `CoTaskMemRealloc` and `CoTaskMemFree` to `com.sun.jna.platform.win32.Ole32` - [@msteiger](https://github.com/msteiger).
* [#334](https://github.com/twall/jna/pull/334): Added `com.sun.jna.platform.win32.Shell32.SHGetKnownFolderPath` and `KnownFolders` GUID constants - [@msteiger](https://github.com/msteiger).
* [#338](https://github.com/twall/jna/pull/338): Added `com.sun.jna.platform.mac.XAttr` and `com.sun.jna.platform.mac.XAttrUtil` JNA wrapper for `<sys/xattr.h>` for Mac OS X - [@rednoah](https://github.com/rednoah)

Bug Fixes
---------
Expand Down
51 changes: 51 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/mac/XAttr.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* Copyright (c) 2014 Reinhard Pointner, All Rights Reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package com.sun.jna.platform.mac;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

/**
* JNA wrapper for <sys/xattr.h>
*
*/
interface XAttr extends Library {

// load from current image
XAttr INSTANCE = (XAttr) Native.loadLibrary(null, XAttr.class);

// see /usr/include/sys/xattr.h
int XATTR_NOFOLLOW = 0x0001;
int XATTR_CREATE = 0x0002;
int XATTR_REPLACE = 0x0004;
int XATTR_NOSECURITY = 0x0008;
int XATTR_NODEFAULT = 0x0010;
int XATTR_SHOWCOMPRESSION = 0x0020;
int XATTR_MAXNAMELEN = 127;
String XATTR_FINDERINFO_NAME = "com.apple.FinderInfo";
String XATTR_RESOURCEFORK_NAME = "com.apple.ResourceFork";

// see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/getxattr.2.html
long getxattr(String path, String name, Pointer value, long size, int position, int options);

// see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/setxattr.2.html
int setxattr(String path, String name, Pointer value, long size, int position, int options);

// see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/removexattr.2.html
int removexattr(String path, String name, int options);

// see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/listxattr.2.html
long listxattr(String path, Pointer namebuff, long size, int options);

}
98 changes: 98 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/mac/XAttrUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* Copyright (c) 2014 Reinhard Pointner, All Rights Reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package com.sun.jna.platform.mac;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import com.sun.jna.Memory;

public class XAttrUtil {

public static List<String> listXAttr(String path) {
// get required buffer size
long bufferLength = XAttr.INSTANCE.listxattr(path, null, 0, 0);

if (bufferLength < 0)
return null;

if (bufferLength == 0)
return new ArrayList<String>(0);

Memory valueBuffer = new Memory(bufferLength);
long valueLength = XAttr.INSTANCE.listxattr(path, valueBuffer, bufferLength, 0);

if (valueLength < 0)
return null;

return decodeStringSequence(valueBuffer.getByteBuffer(0, valueLength));
}

public static String getXAttr(String path, String name) {
// get required buffer size
long bufferLength = XAttr.INSTANCE.getxattr(path, name, null, 0, 0, 0);

if (bufferLength < 0)
return null;

Memory valueBuffer = new Memory(bufferLength);
long valueLength = XAttr.INSTANCE.getxattr(path, name, valueBuffer, bufferLength, 0, 0);

if (valueLength < 0)
return null;

return decodeString(valueBuffer.getByteBuffer(0, valueLength - 1));
}

public static int setXAttr(String path, String name, String value) {
Memory valueBuffer = encodeString(value);
return XAttr.INSTANCE.setxattr(path, name, valueBuffer, valueBuffer.size(), 0, 0);
}

public static int removeXAttr(String path, String name) {
return XAttr.INSTANCE.removexattr(path, name, 0);
}

protected static Memory encodeString(String s) {
// create NULL-terminated UTF-8 String
byte[] bb = s.getBytes(Charset.forName("UTF-8"));
Memory valueBuffer = new Memory(bb.length + 1);
valueBuffer.write(0, bb, 0, bb.length);
valueBuffer.setByte(valueBuffer.size() - 1, (byte) 0);
return valueBuffer;
}

protected static String decodeString(ByteBuffer bb) {
return Charset.forName("UTF-8").decode(bb).toString();
}

protected static List<String> decodeStringSequence(ByteBuffer bb) {
List<String> names = new ArrayList<String>();

bb.mark(); // first key starts from here
while (bb.hasRemaining()) {
if (bb.get() == 0) {
ByteBuffer nameBuffer = (ByteBuffer) bb.duplicate().limit(bb.position() - 1).reset();
if (nameBuffer.hasRemaining()) {
names.add(decodeString(nameBuffer));
}
bb.mark(); // next key starts from here
}
}

return names;
}

}
117 changes: 117 additions & 0 deletions contrib/platform/test/com/sun/jna/platform/mac/XAttrUtilTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/* Copyright (c) 2014 Reinhard Pointner, All Rights Reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package com.sun.jna.platform.mac;

import java.io.File;
import java.util.Arrays;
import java.util.List;

import junit.framework.TestCase;

public class XAttrUtilTest extends TestCase {

String testPath;

protected void setUp() throws Exception {
testPath = File.createTempFile("xattr-test", ".txt").getAbsolutePath();
assertTrue(new File(testPath).exists());
}

protected void tearDown() throws Exception {
new File(testPath).delete();
assertFalse(new File(testPath).exists());
}

public void testListXAttr() {
// no xattr initially
List<String> keys = XAttrUtil.listXAttr(testPath);
assertEquals(0, keys.size());

// set multiple xattr
String[] names = new String[] { "Java", "Native", "Access" };
for (int i = 0; i < names.length; i++) {
// set xattr
XAttrUtil.setXAttr(testPath, names[i], names[i]);

// check if new xattr is listed
keys = XAttrUtil.listXAttr(testPath);
assertEquals(i + 1, keys.size());
assertTrue(keys.contains(names[i]));
}
}

public void testGetXAttr() {
String value = XAttrUtil.getXAttr(testPath, "JNA");
assertNull(value);

XAttrUtil.setXAttr(testPath, "JNA", "Java Native Access");
value = XAttrUtil.getXAttr(testPath, "JNA");

assertEquals(Arrays.toString("Java Native Access".getBytes()), Arrays.toString(value.getBytes()));
}

public void testSetXAttr() {
String value = XAttrUtil.getXAttr(testPath, "JNA");
assertNull(value);

XAttrUtil.setXAttr(testPath, "JNA", "Java Native Access");
value = XAttrUtil.getXAttr(testPath, "JNA");
assertEquals("Java Native Access", value);

XAttrUtil.setXAttr(testPath, "JNA", "is nice");
value = XAttrUtil.getXAttr(testPath, "JNA");
assertEquals("is nice", value);
}

public void testRemoveXAttr() {
XAttrUtil.setXAttr(testPath, "JNA", "Java Native Access");
assertEquals("[JNA]", XAttrUtil.listXAttr(testPath).toString());

// remove xattr
XAttrUtil.removeXAttr(testPath, "JNA");

assertEquals("[]", XAttrUtil.listXAttr(testPath).toString());
}

public void testUnicode() {
String[] names = new String[] { "中文", "にほんご", "Österreichisch", "Française", "Português" };
for (int i = 0; i < names.length; i++) {
// set xattr
XAttrUtil.setXAttr(testPath, names[i], names[i]);

// check if new xattr is listed
List<String> keys = XAttrUtil.listXAttr(testPath);
assertEquals(i + 1, keys.size());
assertTrue(keys.contains(names[i]));

String value = XAttrUtil.getXAttr(testPath, names[i]);
assertEquals(names[i], value);
}
}

public void testLargeData() {
StringBuilder name = new StringBuilder();
while (name.length() < XAttr.XATTR_MAXNAMELEN) {
name.append('X');
}

StringBuilder data = new StringBuilder();
while (data.length() < 4 * 1024 * 1024) {
data.append('X');
}

XAttrUtil.setXAttr(testPath, name.toString(), data.toString());
String value = XAttrUtil.getXAttr(testPath, name.toString());
assertEquals(data.toString(), value.toString());
}
}

0 comments on commit 33dcdb0

Please sign in to comment.