java.io.File.setReadable(false)在Docker内部无效

时间:2018-07-28 11:10:08

标签: java file docker

以下代码在具有OpenJDK 8的Ubuntu 18.04上成功,但在基于OpenJDK 8的Docker映像maven:3-jdk-8-slim内失败:

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());

故障详细信息:

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)

引用assertFalse(file.canRead())断言。此断言应该通过,因为file.setReadable(false)返回了true,因此成功了。

我使用基于Docker的GitLab CI在SSCCE中重现了该问题,因此可以在https://gitlab.com/krichter/docker-java-file-readability/-/jobs/203311757上找到详细的输出。 SSCCE所包含的信息除上述代码摘录外没有更多,但是使本地调查更加容易。

我在寻找解释,而不是解决方法。

3 个答案:

答案 0 :(得分:10)

documentation of File.canRead()包含一条注释,说明其结果可能令人困惑:

  

essential

     

测试应用程序是否可以读取此表示的文件   抽象路径名。 在某些平台上,可能可以启动   具有允许其读取的特殊特权的Java虚拟机   标记为不可读的文件。因此,此方法可能   即使该文件没有读取权限也返回false

在docker进程下,通常以root身份运行,从而赋予普通用户看不到的特权。

证明root可以读取缺乏读取权限的文件:

public boolean canRead()

答案 1 :(得分:0)

我找到了解决此问题的方法。

在您的 docker 中,如果您的 linux 映像可能,您将必须创建一个非管理员用户,然后使用该用户“su”并以该用户身份启动您需要的所有命令。

这是一个仅在您不是管理员或 root 用户时才有效的示例:

# useradd -m myUser
# su - myUser
$ pwd
/home/myUser
$ cat <<EOF > Test.java
> import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class Test {
  public static void main(String[] args) throws IOException {
    Path tempDir = Files.createTempDirectory("Test");
    final File nonReadableFile = new File(tempDir.toFile(), "nonReadableFile.txt");
    System.out.println(nonReadableFile.getAbsolutePath());


    nonReadableFile.createNewFile();
    System.out.println(nonReadableFile.canRead());
    nonReadableFile.setReadable(false);
    System.out.println(nonReadableFile.canRead());
  }
}
> > > > > > > > > > > > > > > > > > > EOF
$ javac Test.java
$ java Test
/tmp/Test2125736191041151487/nonReadableFile.txt
true
false

答案 2 :(得分:-3)

  

此断言应该通过,因为File.canRead()应该在File.setReadable(false)之后返回false。

仅当setReadable()返回true时。当您扔掉返回代码而不是对其进行操作时,您的tedt无效。