From fec9712b151ad31b053fe700cb0f809b9715407c Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Wed, 23 Oct 2013 16:48:42 +0200 Subject: Fix Bug 865: Safari >= 6.1 [OSX]: May employ xattr on 'com.apple.quarantine' on 'PluginProcess.app' - IOUtil.getTempDir(..): Don't test executable caps on OSX for java_io_tmpdir - JarUtil.extract(..): Issue native fixNativeLibAttribs(..) on OSX for native library files, i.e. remove xattr 'com.apple.quarantine' --- make/build.xml | 5 +- src/java/com/jogamp/common/util/IOUtil.java | 97 +++++++++++++++------- src/java/com/jogamp/common/util/JarUtil.java | 28 +++++++ .../jogamp/common/util/cache/TempFileCache.java | 2 +- src/native/common/JarUtil.c | 39 +++++++++ 5 files changed, 138 insertions(+), 33 deletions(-) create mode 100644 src/native/common/JarUtil.c diff --git a/make/build.xml b/make/build.xml index 554ba43..55ef270 100644 --- a/make/build.xml +++ b/make/build.xml @@ -457,10 +457,7 @@ - - - - + diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java index ef6a633..45cb6ec 100644 --- a/src/java/com/jogamp/common/util/IOUtil.java +++ b/src/java/com/jogamp/common/util/IOUtil.java @@ -1006,9 +1006,15 @@ public class IOUtil { throws SecurityException { if (!testFile(dir, true, true)) { + if(DEBUG) { + System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Not writeable dir"); + } return false; } if(!getOSHasNoexecFS()) { + if(DEBUG) { + System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Always executable"); + } return true; } @@ -1023,40 +1029,45 @@ public class IOUtil { } return false; } - int ok = -1; - if(exetst.setExecutable(true)) { + int res = -1; + if(exetst.setExecutable(true /* exec */, true /* ownerOnly */)) { try { Process pr = Runtime.getRuntime().exec(exetst.getCanonicalPath()); pr.waitFor() ; - ok = pr.exitValue(); + res = pr.exitValue(); } catch (SecurityException se) { throw se; // fwd Security exception } catch (Throwable t) { - ok = -2; + res = -2; if(DEBUG) { - System.err.println("IOUtil.testDirExec: <"+exetst.getAbsolutePath()+">: "+t.getMessage()); + System.err.println("IOUtil.testDirExec: <"+exetst.getAbsolutePath()+">: Catched "+t.getClass().getSimpleName()+": "+t.getMessage()); // t.printStackTrace(); } } } exetst.delete(); - return 0 == ok; + if(DEBUG) { + System.err.println("IOUtil.testDirExec(): <"+dir.getAbsolutePath()+">: res "+res); + } + return 0 == res; } - private static File testDirImpl(File dir, boolean create, boolean executable) + private static File testDirImpl(File dir, boolean create, boolean executable, String dbgMsg) throws SecurityException { + final File res; if (create && !dir.exists()) { dir.mkdirs(); } if( executable ) { - if(testDirExec(dir)) { - return dir; - } - } else if(testFile(dir, true, true)) { - return dir; + res = testDirExec(dir) ? dir : null; + } else { + res = testFile(dir, true, true) ? dir : null; } - return null; + if(DEBUG) { + System.err.println("IOUtil.testDirImpl("+dbgMsg+"): <"+dir.getAbsolutePath()+">, create "+create+", exec "+executable+": "+(null != res)); + } + return res; } /** @@ -1076,7 +1087,7 @@ public class IOUtil { public static File testDir(final File dir, final boolean create, final boolean executable) throws SecurityException { - return testDirImpl(dir, create, executable); + return testDirImpl(dir, create, executable, "testDir"); } private static boolean isStringSet(String s) { return null != s && 0 < s.length(); } @@ -1100,18 +1111,19 @@ public class IOUtil { *

* @param tmpRoot * @param executable + * @param dbgMsg TODO * @param tmpDirPrefix * @return a temporary directory, writable by this user * @throws SecurityException */ - private static File getSubTempDir(File tmpRoot, String tmpSubDirPrefix, boolean executable) + private static File getSubTempDir(File tmpRoot, String tmpSubDirPrefix, boolean executable, String dbgMsg) throws SecurityException { File tmpBaseDir = null; - if(null != testDirImpl(tmpRoot, true /* create */, executable)) { // check tmpRoot first + if(null != testDirImpl(tmpRoot, true /* create */, executable, dbgMsg)) { // check tmpRoot first for(int i = 0; null == tmpBaseDir && i<=9999; i++) { final String tmpDirSuffix = String.format("_%04d", i); // 4 digits for iteration - tmpBaseDir = testDirImpl(new File(tmpRoot, tmpSubDirPrefix+tmpDirSuffix), true /* create */, executable); + tmpBaseDir = testDirImpl(new File(tmpRoot, tmpSubDirPrefix+tmpDirSuffix), true /* create */, executable, dbgMsg); } } return tmpBaseDir; @@ -1153,13 +1165,25 @@ public class IOUtil { { final File ctxTempDir = AndroidUtils.getTempRoot(); // null if ( !Android || no android-ctx ) if(null != ctxTempDir) { - tempRootNoexec = getSubTempDir(ctxTempDir, tmpSubDir, false /* executable, see below */); + tempRootNoexec = getSubTempDir(ctxTempDir, tmpSubDir, false /* executable, see below */, "Android.ctxTemp"); tempRootExec = tempRootNoexec; // FIXME: Android temp root is always executable (?) return tempRootExec; } } final String java_io_tmpdir = PropertyAccess.getProperty(java_io_tmpdir_propkey, false); + final String user_temp; // only if diff than java_io_tmpdir + { + String _user_temp = System.getenv("TMPDIR"); + if( !isStringSet(_user_temp) ) { + _user_temp = System.getenv("TEMP"); + } + if( isStringSet(_user_temp) && !_user_temp.equals(java_io_tmpdir) ) { + user_temp = _user_temp; + } else { + user_temp = null; + } + } final String user_home = PropertyAccess.getProperty(user_home_propkey, false); final String xdg_cache_home; @@ -1178,41 +1202,58 @@ public class IOUtil { // 1) java.io.tmpdir/jogamp if( null == tempRootExec && isStringSet(java_io_tmpdir) ) { - tempRootExec = getSubTempDir(new File(java_io_tmpdir), tmpSubDir, true /* executable */); + if( Platform.OSType.MACOS == Platform.getOSType() ) { + // Bug 865: Safari >= 6.1 [OSX] May employ xattr on 'com.apple.quarantine' on 'PluginProcess.app' + // We attempt to fix this issue _after_ gluegen native lib is loaded, see JarUtil.fixNativeLibAttribs(File). + tempRootExec = getSubTempDir(new File(java_io_tmpdir), tmpSubDir, false /* executable */, "tempX1"); + } else { + tempRootExec = getSubTempDir(new File(java_io_tmpdir), tmpSubDir, true /* executable */, "tempX1"); + } } // 2) $XDG_CACHE_HOME/jogamp if(null == tempRootExec && isStringSet(xdg_cache_home)) { - tempRootExec = getSubTempDir(new File(xdg_cache_home), tmpSubDir, true /* executable */); + tempRootExec = getSubTempDir(new File(xdg_cache_home), tmpSubDir, true /* executable */, "tempX2"); } - // 3) $HOME/.jogamp - if(null == tempRootExec && isStringSet(user_home)) { - tempRootExec = getSubTempDir(new File(user_home), "." + tmpSubDir, true /* executable */); + // 3) $TMPDIR/jogamp + if(null == tempRootExec && isStringSet(user_temp)) { + tempRootExec = getSubTempDir(new File(user_temp), tmpSubDir, true /* executable */, "tempX3"); } + // 4) $HOME/.jogamp + if(null == tempRootExec && isStringSet(user_home)) { + tempRootExec = getSubTempDir(new File(user_home), "." + tmpSubDir, true /* executable */, "tempX4"); + } if(null != tempRootExec) { tempRootNoexec = tempRootExec; } else { // 1) java.io.tmpdir/jogamp if( null == tempRootNoexec && isStringSet(java_io_tmpdir) ) { - tempRootNoexec = getSubTempDir(new File(java_io_tmpdir), tmpSubDir, false /* executable */); + tempRootNoexec = getSubTempDir(new File(java_io_tmpdir), tmpSubDir, false /* executable */, "temp01"); } // 2) $XDG_CACHE_HOME/jogamp if(null == tempRootNoexec && isStringSet(xdg_cache_home)) { - tempRootNoexec = getSubTempDir(new File(xdg_cache_home), tmpSubDir, false /* executable */); + tempRootNoexec = getSubTempDir(new File(xdg_cache_home), tmpSubDir, false /* executable */, "temp02"); + } + + // 3) $TMPDIR/jogamp + if(null == tempRootNoexec && isStringSet(user_temp)) { + tempRootNoexec = getSubTempDir(new File(user_temp), tmpSubDir, false /* executable */, "temp03"); } - // 3) $HOME/.jogamp + // 4) $HOME/.jogamp if(null == tempRootNoexec && isStringSet(user_home)) { - tempRootNoexec = getSubTempDir(new File(user_home), "." + tmpSubDir, false /* executable */); + tempRootNoexec = getSubTempDir(new File(user_home), "." + tmpSubDir, false /* executable */, "temp04"); } } if(DEBUG) { - System.err.println("IOUtil.getTempRoot(): temp dirs: exec: "+tempRootExec.getAbsolutePath()+", noexec: "+tempRootNoexec.getAbsolutePath()); + final String tempRootExecAbsPath = null != tempRootExec ? tempRootExec.getAbsolutePath() : null; + final String tempRootNoexecAbsPath = null != tempRootNoexec ? tempRootNoexec.getAbsolutePath() : null; + System.err.println("IOUtil.getTempRoot(): temp dirs: exec: "+tempRootExecAbsPath+", noexec: "+tempRootNoexecAbsPath); } } } diff --git a/src/java/com/jogamp/common/util/JarUtil.java b/src/java/com/jogamp/common/util/JarUtil.java index 5411d66..b5f8850 100644 --- a/src/java/com/jogamp/common/util/JarUtil.java +++ b/src/java/com/jogamp/common/util/JarUtil.java @@ -47,6 +47,7 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import com.jogamp.common.os.NativeLibrary; +import com.jogamp.common.os.Platform; import jogamp.common.Debug; @@ -632,6 +633,7 @@ public class JarUtil { if (isNativeLib && ( isRootEntry || !nativeLibMap.containsKey(libBaseName) ) ) { nativeLibMap.put(libBaseName, destFile.getAbsolutePath()); addedAsNativeLib = true; + fixNativeLibAttribs(destFile); } } if (DEBUG) { @@ -642,6 +644,32 @@ public class JarUtil { return num; } + /** + * Mitigate file permission issues of native library files, i.e.: + *
    + *
  • Bug 865: Safari >= 6.1 [OSX]: May employ xattr on 'com.apple.quarantine' on 'PluginProcess.app'
  • + *
+ */ + private final static void fixNativeLibAttribs(File file) { + // We tolerate UnsatisfiedLinkError (and derived) to solve the chicken and egg problem + // of loading gluegen's native library. + // On Safari(OSX), Bug 865, we simply hope the destination folder is executable. + if( Platform.OSType.MACOS == Platform.getOSType() ) { + final String fileAbsPath = file.getAbsolutePath(); + try { + fixNativeLibAttribs(fileAbsPath); + if( DEBUG ) { + System.err.println("JarUtil.fixNativeLibAttribs: "+fileAbsPath+" - OK"); + } + } catch (Throwable t) { + if( DEBUG ) { + System.err.println("JarUtil.fixNativeLibAttribs: "+fileAbsPath+" - "+t.getClass().getSimpleName()+": "+t.getMessage()); + } + } + } + } + private native static boolean fixNativeLibAttribs(String fname); + /** * Validate the certificates for each native Lib in the jar file. * Throws an IOException if any certificate is not valid. diff --git a/src/java/com/jogamp/common/util/cache/TempFileCache.java b/src/java/com/jogamp/common/util/cache/TempFileCache.java index cc7014a..3fd0a59 100644 --- a/src/java/com/jogamp/common/util/cache/TempFileCache.java +++ b/src/java/com/jogamp/common/util/cache/TempFileCache.java @@ -80,7 +80,7 @@ public class TempFileCache { _tmpBaseDir = new File(IOUtil.getTempDir(true /* executable */), tmpDirPrefix); _tmpBaseDir = IOUtil.testDir(_tmpBaseDir, true /* create */, false /* executable */); // executable already checked } catch (Exception ex) { - System.err.println("Warning: Catched Exception while retrieving temp base directory:"); + System.err.println("Warning: Catched Exception while retrieving executable temp base directory:"); ex.printStackTrace(); staticInitError = true; } diff --git a/src/native/common/JarUtil.c b/src/native/common/JarUtil.c new file mode 100644 index 0000000..c918f77 --- /dev/null +++ b/src/native/common/JarUtil.c @@ -0,0 +1,39 @@ +#include + +#include + +#include "com_jogamp_common_util_JarUtil.h" + +#if defined(__APPLE__) + #include + static const char kQuarantineAttrName[] = "com.apple.quarantine"; +#endif + +/* + * Class: com_jogamp_common_util_JarUtil + * Method: fixNativeLibAttribs + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_com_jogamp_common_util_JarUtil_fixNativeLibAttribs + (JNIEnv *env, jclass _unused, jstring fname) { + const char* _UTF8fname = NULL; + int status = 0; + if (fname != NULL) { + if (fname != NULL) { + _UTF8fname = (*env)->GetStringUTFChars(env, fname, (jboolean*)NULL); + if (_UTF8fname == NULL) { + (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/OutOfMemoryError"), + "Failed to get UTF-8 chars for argument \"fname\" in native dispatcher for \"removexattr\""); + return 0; + } + } + } +#if defined(__APPLE__) + status = removexattr(_UTF8fname, kQuarantineAttrName, 0); +#endif + if (fname != NULL) { + (*env)->ReleaseStringUTFChars(env, fname, _UTF8fname); + } + return 0 == status ? JNI_TRUE : JNI_FALSE; +} + -- cgit v1.2.3