Title: Add ability for static search locations · Issue #41 · scijava/native-lib-loader · GitHub
Open Graph Title: Add ability for static search locations · Issue #41 · scijava/native-lib-loader
X Title: Add ability for static search locations · Issue #41 · scijava/native-lib-loader
Description: Summary The native-lib-loader should allow specifying a library in a static location for edge-case scenarios, usually imposed by security restrictions, constraints or sandboxing. Edit: Static locations may also offer slight performance b...
Open Graph Description: Summary The native-lib-loader should allow specifying a library in a static location for edge-case scenarios, usually imposed by security restrictions, constraints or sandboxing. Edit: Static locat...
X Description: Summary The native-lib-loader should allow specifying a library in a static location for edge-case scenarios, usually imposed by security restrictions, constraints or sandboxing. Edit: Static locat...
Opengraph URL: https://github.com/scijava/native-lib-loader/issues/41
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Add ability for static search locations","articleBody":"### Summary\r\n\r\nThe `native-lib-loader` should allow specifying a library in a static location for edge-case scenarios, usually imposed by security restrictions, constraints or sandboxing.\r\n\r\n**Edit:** Static locations may also offer slight performance benefits as well, eliminating the need for unzipping (CPU/disk) activity, see discussion [here](https://github.com/scijava/native-lib-loader/pull/32#issuecomment-887728011).\r\n\r\n### Details\r\n\r\nTechnically, `native-lib-loader` allows for static search locations, but it requires implementing the `JniExtractor` class or overriding the behavior of the `DefaultJniExractor` class, as -- by design -- they both intend to perform an extract operation prior to loading a native library. This extraction operation doesn't work in all environments.\r\n\r\nI'll quote a sister project -- JNA -- documentation, I think it words this problem well and explains as to why this can be needed... \r\n\r\n\u003e * When [...] classes are loaded, the native shared library [...] is loaded as well. An attempt is made to load it from the any paths defined in `jna.boot.library.path` (if defined), then the system library path using `System.loadLibrary(java.lang.String)`, unless `jna.nosys=true`.\r\n\u003e * If not found, the appropriate library will be extracted from the class path (into a temporary directory if found within a jar file) and loaded from there, unless `jna.noclasspath=true`.\r\n\u003e * If your system has additional security constraints regarding execution or load of files (`SELinux`, for example), **you should probably install the native library in an accessible location and configure your system accordingly, rather than relying on JNA to extract the library from its own jar file**.\r\n\r\nThe last bullet is the point I'd like to focus on since this same use-case exists for `native-lib-loader`. For example, if a native library is intended to be distributed with a Java application that's distributed from the Apple AppStore, sandboxing is a hard-requirement, and like the aforementioned SELinux use-case, sandboxing can prevent loading a library from arbitrary locations (such as $TEMP), quoting an Apple employee on the [Apple Developer forums](https://developer.apple.com/forums/thread/22833?answerId=75935022#75935022):\r\n\r\n\u003e I think you’ll run into problems there. Specifically, modern versions of the system prevent an app from referencing libraries outside of the app (other than system libraries). See Gatekeeper Changes in OS X `v10.10.4` and Later in [Technote 2206 OS X Code Signing In Depth](https://developer.apple.com/library/mac/technotes/tn2206/_index.html#//apple_ref/doc/uid/DTS40007919-CH1-TNTAG207)\r\n\r\nFurthermore, the JNA portion \"unless `jna.nosys=true`\", I find to be increasingly important as client environments may have identically named libraries in search paths that the client has little or no control over. This is a separate issue, but may be tackled as part of the same enhancement.\r\n\r\n### Workaround\r\n\r\nProviding a stub extractor can handle this issue:\r\n\r\n\u003cdetails\u003e\r\n\u003csummary\u003eClick to see \u003ccode\u003eDefaultJniExtractorStub.java\u003c/code\u003e\u003c/summary\u003e\r\n\r\n```java\r\n\r\n/**\r\n * License: https://opensource.org/licenses/BSD-3-Clause\r\n */\r\npackage jssc;\r\n\r\nimport org.scijava.nativelib.DefaultJniExtractor;\r\nimport org.scijava.nativelib.NativeLibraryUtil;\r\n\r\nimport java.io.File;\r\nimport java.io.IOException;\r\n\r\n/**\r\n * @author A. Tres Finocchiaro\r\n *\r\n * Stub \u003ccode\u003eDefaultJniExtractor\u003c/code\u003e class to allow native-lib-loader to conditionally\r\n * use a statically defined native search path \u003ccode\u003ebootPath\u003c/code\u003e when provided.\r\n */\r\npublic class DefaultJniExtractorStub extends DefaultJniExtractor {\r\n private File bootPath;\r\n private boolean useStub;\r\n\r\n /**\r\n * Default constructor\r\n */\r\n public DefaultJniExtractorStub(Class libraryJarClass) throws IOException {\r\n super(libraryJarClass);\r\n useStub = false;\r\n }\r\n\r\n /**\r\n * Force native-lib-loader to first look in the location defined as \u003ccode\u003ebootPath\u003c/code\u003e\r\n * prior to extracting a native library, useful for sandboxed environments.\r\n * \u003ccode\u003e\r\n * NativeLoader.setJniExtractor(new DefaultJniExtractorStub(null, \"/opt/nativelibs\")));\r\n * NativeLoader.loadLibrary(\"mylibrary\");\r\n * \u003c/code\u003e\r\n */\r\n public DefaultJniExtractorStub(Class libraryJarClass, String bootPath) throws IOException {\r\n this(libraryJarClass);\r\n this.bootPath = new File(bootPath);\r\n\r\n if(bootPath != null) {\r\n File bootTest = new File(bootPath);\r\n if(bootTest.exists()) {\r\n // assume a static, existing directory will contain the native libs\r\n this.useStub = true;\r\n } else {\r\n System.err.println(\"WARNING \" + DefaultJniExtractorStub.class.getCanonicalName() + \": Boot path \" + bootPath + \" not found, falling back to default extraction behavior.\");\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * If a \u003ccode\u003ebootPath\u003c/code\u003e was provided to the constructor and exists,\r\n * calculate the \u003ccode\u003eFile\u003c/code\u003e path without any extraction logic.\r\n *\r\n * If a \u003ccode\u003ebootPath\u003c/code\u003e was NOT provided or does NOT exist, fallback on\r\n * the default extraction behavior.\r\n */\r\n @Override\r\n public File extractJni(String libPath, String libName) throws IOException {\r\n // Lie and pretend it's already extracted at the bootPath location\r\n if(useStub) {\r\n return new File(bootPath, NativeLibraryUtil.getPlatformLibraryName(libName));\r\n }\r\n // Fallback on default behavior\r\n return super.extractJni(libPath, libName);\r\n }\r\n\r\n @Override\r\n public void extractRegistered() throws IOException {\r\n if(useStub) {\r\n return; // no-op\r\n }\r\n super.extractRegistered();\r\n }\r\n}\r\n\r\n```\r\n\r\n\u003c/details\u003e\r\n\r\nUsage:\r\n\r\n```diff\r\n+ NativeLoader.setJniExtractor(new DefaultJniExtractorStub(null, \"/opt/libs\"));\r\n NativeLoader.loadLibrary(\"mylibrary\");\r\n```\r\n\r\n### Caveats\r\n\r\nDue to the library loading order in `native-lib-loader`, any [System locations will be always be preferred](https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#loadLibrary(java.lang.String)), which can cause compatibility issues if the system was modified (probably warrants a separate bug report).\r\n\r\nhttps://github.com/scijava/native-lib-loader/blob/56cdf6200592e06562cc099e583b1dbbc97db6df/src/main/java/org/scijava/nativelib/NativeLoader.java#L134-L142","author":{"url":"https://github.com/tresf","@type":"Person","name":"tresf"},"datePublished":"2021-07-27T16:57:24.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":3},"url":"https://github.com/41/native-lib-loader/issues/41"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:27fabc1f-7f88-0b83-6b38-f2a9db3ff0c7 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 8530:5177D:172F3D2:2074427:69692233 |
| html-safe-nonce | 57f8b0f98820a551cb704dba98433315f8a73c2b79b724b405d174374cf52c94 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4NTMwOjUxNzdEOjE3MkYzRDI6MjA3NDQyNzo2OTY5MjIzMyIsInZpc2l0b3JfaWQiOiI2MDIzMTU5OTI2MDQ3MTg3NTA3IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 58747e8a3b596a6757685efd7b7f2bb88294946bd87b8abcf0ed9f58de547755 |
| hovercard-subject-tag | issue:954078648 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/scijava/native-lib-loader/41/issue_layout |
| twitter:image | https://opengraph.githubassets.com/6c939939f810346c318e487781bf71f82a0fc5effe83170061e7e697285184a8/scijava/native-lib-loader/issues/41 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/6c939939f810346c318e487781bf71f82a0fc5effe83170061e7e697285184a8/scijava/native-lib-loader/issues/41 |
| og:image:alt | Summary The native-lib-loader should allow specifying a library in a static location for edge-case scenarios, usually imposed by security restrictions, constraints or sandboxing. Edit: Static locat... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | tresf |
| hostname | github.com |
| expected-hostname | github.com |
| None | 54182691a21263b584d2e600b758e081b0ff1d10ffc0d2eefa51cf754b43b51d |
| turbo-cache-control | no-preview |
| go-import | github.com/scijava/native-lib-loader git https://github.com/scijava/native-lib-loader.git |
| octolytics-dimension-user_id | 1262770 |
| octolytics-dimension-user_login | scijava |
| octolytics-dimension-repository_id | 3805074 |
| octolytics-dimension-repository_nwo | scijava/native-lib-loader |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 3805074 |
| octolytics-dimension-repository_network_root_nwo | scijava/native-lib-loader |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | d69ac0477df0f87da03b8b06cebd187012d7a930 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width