* replace xattrj with my own JNA wrapper for <sys/xattr.h>

This commit is contained in:
Reinhard Pointner 2014-06-18 09:28:27 +00:00
parent 12d04c1681
commit 977452268e
6 changed files with 201 additions and 49 deletions

View File

@ -157,7 +157,7 @@
<include name="META-INF/services/**" /> <include name="META-INF/services/**" />
<include name="META-INF/*.properties" /> <include name="META-INF/*.properties" />
<!-- filebot already includes it's own extension modules --> <!-- filebot already includes it's own extension modules -->
<exclude name="META-INF/services/org.codehaus.groovy.runtime.ExtensionModule"/> <exclude name="META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" />
</zipfileset> </zipfileset>
<zipfileset src="${dir.lib}/icu4j.jar"> <zipfileset src="${dir.lib}/icu4j.jar">
@ -182,10 +182,6 @@
<include name="net/sf/sevenzipjbinding/**" /> <include name="net/sf/sevenzipjbinding/**" />
</zipfileset> </zipfileset>
<zipfileset src="${dir.lib}/xattrj.jar">
<include name="org/securityvision/**" />
</zipfileset>
<!-- Ivy for @Grapes automatic dependency management --> <!-- Ivy for @Grapes automatic dependency management -->
<zipfileset src="${dir.lib}/scripting/ivy.jar"> <zipfileset src="${dir.lib}/scripting/ivy.jar">
<include name="org/apache/ivy/**" /> <include name="org/apache/ivy/**" />

Binary file not shown.

View File

@ -1,6 +1,7 @@
package net.filebot; package net.filebot;
import static java.nio.file.Files.*; import static java.nio.file.Files.isSymbolicLink;
import static java.nio.file.Files.readSymbolicLink;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -12,31 +13,20 @@ import java.nio.file.attribute.UserDefinedFileAttributeView;
import java.text.Normalizer; import java.text.Normalizer;
import java.text.Normalizer.Form; import java.text.Normalizer.Form;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.Arrays;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.securityvision.xattrj.Xattrj; import net.filebot.mac.xattr.XattrUtil;
import com.sun.jna.Platform; import com.sun.jna.Platform;
public class MetaAttributeView extends AbstractMap<String, String> { public class MetaAttributeView extends AbstractMap<String, String> {
// UserDefinedFileAttributeView (for Windows and Linux) OR 3rd party Xattrj (for Mac) because UserDefinedFileAttributeView is not supported (Oracle Java 7/8) // UserDefinedFileAttributeView (for Windows and Linux) OR our own xattr.h JNA wrapper via MacXattrView (for Mac) because UserDefinedFileAttributeView is not supported (Oracle Java 7/8)
private Object xattr; private Object xattr;
private File file;
private Charset encoding; private Charset encoding;
private static Xattrj MacXattrSupport;
private static synchronized Xattrj getMacXattrSupport() throws Throwable {
if (MacXattrSupport == null) {
MacXattrSupport = new Xattrj();
}
return MacXattrSupport;
}
public MetaAttributeView(File file) throws IOException { public MetaAttributeView(File file) throws IOException {
Path path = file.getCanonicalFile().toPath(); Path path = file.getCanonicalFile().toPath();
while (isSymbolicLink(path)) { while (isSymbolicLink(path)) {
@ -49,25 +39,14 @@ public class MetaAttributeView extends AbstractMap<String, String> {
xattr = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class); xattr = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
// try 3rd party Xattrj library
if (xattr == null) { if (xattr == null) {
if (Platform.isMac()) { if (Platform.isMac()) {
// Xattrj for Mac xattr = new MacXattrView(path);
try {
xattr = getMacXattrSupport();
} catch (Throwable e) {
throw new IOException("Unable to load library: xattrj", e);
}
// MacOS filesystem may require NFD unicode decomposition
this.file = new File(Normalizer.normalize(path.toFile().getAbsolutePath(), Form.NFD));
this.encoding = Charset.forName("UTF-8");
} else { } else {
throw new IOException("UserDefinedFileAttributeView is not supported"); throw new IOException("UserDefinedFileAttributeView is not supported");
} }
} else { } else {
// UserDefinedFileAttributeView // UserDefinedFileAttributeView
this.file = path.toFile();
this.encoding = Charset.forName("UTF-8"); this.encoding = Charset.forName("UTF-8");
} }
} }
@ -84,9 +63,9 @@ public class MetaAttributeView extends AbstractMap<String, String> {
return encoding.decode(buffer).toString(); return encoding.decode(buffer).toString();
} }
if (xattr instanceof Xattrj) { if (xattr instanceof MacXattrView) {
Xattrj macXattr = (Xattrj) xattr; MacXattrView macXattr = (MacXattrView) xattr;
return macXattr.readAttribute(file, key.toString()); return macXattr.read(key.toString());
} }
} catch (Exception e) { } catch (Exception e) {
// ignore // ignore
@ -107,12 +86,12 @@ public class MetaAttributeView extends AbstractMap<String, String> {
} }
} }
if (xattr instanceof Xattrj) { if (xattr instanceof MacXattrView) {
Xattrj macXattr = (Xattrj) xattr; MacXattrView macXattr = (MacXattrView) xattr;
if (value == null || value.isEmpty()) { if (value == null || value.isEmpty()) {
macXattr.removeAttribute(file, key); macXattr.delete(key);
} else { } else {
macXattr.writeAttribute(file, key, value); macXattr.write(key, value);
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -128,9 +107,9 @@ public class MetaAttributeView extends AbstractMap<String, String> {
return attributeView.list(); return attributeView.list();
} }
if (xattr instanceof Xattrj) { if (xattr instanceof MacXattrView) {
Xattrj macXattr = (Xattrj) xattr; MacXattrView macXattr = (MacXattrView) xattr;
return Arrays.asList(macXattr.listAttributes(file)); return macXattr.list();
} }
return null; return null;
@ -189,4 +168,30 @@ public class MetaAttributeView extends AbstractMap<String, String> {
} }
} }
private static class MacXattrView {
private final String path;
public MacXattrView(Path path) {
// MacOS filesystem may require NFD unicode decomposition
this.path = Normalizer.normalize(path.toFile().getAbsolutePath(), Form.NFD);
}
public List<String> list() {
return XattrUtil.listXAttr(path);
}
public String read(String key) {
return XattrUtil.getXAttr(path, key);
}
public void write(String key, String value) {
XattrUtil.setXAttr(path, key, value);
}
public void delete(String key) {
XattrUtil.removeXAttr(path, key);
}
}
} }

View File

@ -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 net.filebot.mac.xattr;
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);
}

View File

@ -0,0 +1,100 @@
/* 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 net.filebot.mac.xattr;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
public class XattrUtil {
public static List<String> listXAttr(String path) {
// get required buffer size
long bufferLength = XAttr.INSTANCE.listxattr(path, Pointer.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, Pointer.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;
}
}