diff --git a/README.md b/README.md index f0c3dd3..5981178 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,29 @@ If using features that rely on OpenCV (see the [Downsampling type](#downsampling __NOTE:__ If you are setting `jna.library.path` via the `JAVA_OPTS` environment variable, make sure the path is to the folder __containing__ the library not path to the library itself. +Temporary directory usage +========================= + +Beginning with 0.10.0, if the default temporary directory (usually `/tmp/`) is mounted as `noexec`, conversion will fail immediately by default. +[CIS security benchmarks](https://www.cisecurity.org/benchmark) recommend that `/tmp/` be mounted as `noexec`; these standards are increasingly +being adopted by major Linux distributions. For certain types of input data (e.g. NDPI, JPEG-XR compression), bioformats2raw needs +to extract a native library from one or more jars to a temporary location. In these cases, the extracted native library must be executable in order +to read image data. + +The recommended solution is to choose a different temporary directory by adding `-Djava.io.tmpdir=/path/to/alternate/tmp` to `JAVA_OPTS`. +If multiple properties need to be set via `JAVA_OPTS`, separate them with a space, e.g. `JAVA_OPTS="-Djava.io.tmpdir/path/to/alternate/tmp -Djna.library.path=/path/to/blosc"`. + +If you know this issue does not affect your input data and wish to warn instead of immediately stopping conversion, the `--warn-no-exec` option can be used. +For input data that relies on native library loading (e.g. NDPI, JPEG-XR compression), using `--warn-no-exec` instead of specifying an alternate +temporary directory will simply cause the conversion to fail at a later point. + +For additional information, please see: + +* https://github.com/glencoesoftware/bioformats2raw/pull/252 +* https://github.com/scijava/native-lib-loader/issues/41 +* https://forum.image.sc/t/after-omero-server-upgrade-hamamatsu-ndpi-files-display-only-in-low-resolution/81868 +* https://forum.image.sc/t/bioformats-unable-to-read-czi-files-with-jpegxr-compression-on-almalinux-os-java-lang-unsatisfiedlinkerror-ome-jxrlib-jxrjni-new-decodecontext-j/82646 + Installation ============ diff --git a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java index ca54f63..c86a8f6 100644 --- a/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java +++ b/src/main/java/com/glencoesoftware/bioformats2raw/Converter.java @@ -128,6 +128,8 @@ public class Converter implements Callable { private volatile Path inputPath; private volatile String outputLocation; + private volatile boolean warnNoExec = false; + private Map outputOptions; private volatile Integer pyramidResolutions; private volatile List seriesList; @@ -409,6 +411,23 @@ public void setProgressBars(boolean useProgressBars) { progressBars = useProgressBars; } + /** + * Configure whether to warn instead of throwing an exception + * if files created in the temporary directory + * ("java.io.tmpdir" system property) will not be executable. + * + * @param warnOnly true if a warning should be logged instead of an exception + */ + @Option( + names = {"--warn-no-exec"}, + description = "Warn instead of throwing an exception if " + + "java.io.tmpdir is not executable", + defaultValue = "false" + ) + public void setWarnOnNoExec(boolean warnOnly) { + warnNoExec = warnOnly; + } + /** * Configure whether to print version information and exit * without converting. @@ -944,6 +963,14 @@ public boolean getProgressBars() { return progressBars; } + /** + * @return true if a warning is logged instead of an exception when the tmp + * directory is noexec + */ + public boolean getWarnNoExec() { + return warnNoExec; + } + /** * @return true if only version info is displayed */ @@ -1172,6 +1199,37 @@ public Integer call() throws Exception { tileWidth, tileHeight); } + // if the tmpdir is mounted as noexec, then any native library + // loading (OpenCV, turbojpeg, etc.) is expected to fail, which + // can cause conversion to fail with an exception that is not user friendly + // + // try to detect this case early and fail before the conversion begins, + // with a more informative message + + // a temp file is created and set as executable + // in the noexec case, setting as executable is expected to silently fail + File tmpdirCheck = File.createTempFile("noexec-test", ".txt"); + try { + // expect 'success' to be true in the noexec case, even though + // the file will not actually be executable + boolean success = tmpdirCheck.setExecutable(true); + if (!success || !tmpdirCheck.canExecute()) { + String msg = System.getProperty("java.io.tmpdir") + + " is noexec; fix it or specify a different java.io.tmpdir." + + " See https://github.com/glencoesoftware/bioformats2raw/" + + "blob/master/README.md#temporary-directory-usage for details"; + if (getWarnNoExec()) { + LOGGER.warn(msg); + } + else { + throw new RuntimeException(msg); + } + } + } + finally { + tmpdirCheck.delete(); + } + OpenCVTools.loadOpenCV(); if (progressBars) {