The following code succeeds on Ubuntu 18.04 with OpenJDK 8, but fails inside the Docker image maven:3-jdk-8-slim
which is based on OpenJDK 8:
String userHome = System.getProperty("user.home");
System.out.println(String.format("system property user.home: %s",
userHome));
File file = new File(userHome, "file");
if(!file.createNewFile()) {
throw new IOException("test arrangement failed");
}
if(!file.setReadable(false)) {
throw new IOException("test arrangement failed");
}
assertFalse(file.canRead());
Failure details:
java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:86)
at org.junit.Assert.assertTrue(Assert.java:41)
at org.junit.Assert.assertFalse(Assert.java:64)
at org.junit.Assert.assertFalse(Assert.java:74)
at de.richtercloud.docker.java.file.readability.TheTest.testSomeMethod(TheTest.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
which refers to the assertFalse(file.canRead())
assertion. This assertion should pass because file.setReadable(false)
returned true
and thus succeeded.
I reproduced the issue in a SSCCE with the Docker-based GitLab CI, so that detailed output can be found at https://gitlab.com/krichter/docker-java-file-readability/-/jobs/203311757. The SSCCE does not contain more information than the above code excerpt, but makes local investigation easier.
I'm looking for an explanation, not a workaround.
The documentation of File.canRead()
contains a note that its result may be confusing:
public boolean canRead()
Tests whether the application can read the file denoted by this abstract pathname. On some platforms it may be possible to start the Java virtual machine with special privileges that allow it to read files that are marked as unreadable. Consequently this method may return
true
even though the file does not have read permissions.
Under docker processes usually run as root giving them privileges unseen by regular users.
Proof that root can read files lacking read permission:
$ echo abcd > somefile
$ ls -l somefile
-rw-rw-r-- 1 leon leon 5 Aug 26 21:43 somefile
$ cat somefile
abcd
$ chmod a-rw somefile
$ ls -l somefile
---------- 1 leon leon 5 Aug 26 21:43 somefile
$ cat somefile
cat: somefile: Permission denied
$ sudo cat somefile
abcd