首页 > 解决方案 > 为什么 Linux 不执行具有 Java 执行权限的 bash 脚本?

问题描述

有很多看起来与此类似的问题,但似乎没有一个问题完全相同,而且他们的所有解决方案都对我不起作用,所以这里......

我有一个尝试自动更新自身的 Java 程序。它是这样工作的:

如果一切正常,bash 脚本现在将启动 Java 主类。

此列表中的所有内容实际上都有效,除了最后一步我尝试在最后执行 bash 脚本。

bash 文件权限如下所示(在我运行自动更新程序之前和之后):

-rwxr-xr-x

据我所知,运行这个文件应该没有权限问题(事实上,我可以在进程崩溃后立即手动运行它)。

这是我得到的错误:

java.io.IOException: Cannot run program "/home/xxx/build/image/bin/run": error=13, Permission denied
at java.base/java.lang.ProcessBuilder.start(Unknown Source)
at java.base/java.lang.ProcessBuilder.start(Unknown Source)
at java.base/java.lang.Runtime.exec(Unknown Source)
at java.base/java.lang.Runtime.exec(Unknown Source)
at java.base/java.lang.Runtime.exec(Unknown Source)
at my_mod/my.Main.main(Unknown Source)

我写了一个类似的“过程炸弹”,但它有效:

所以我认为这不是某种针对此类进程炸弹的 Linux 保护。

但我完全不明白为什么 Linux 在更新后仍然拒绝访问运行该 bash 脚本。

我的操作系统是 Ubuntu 19。

我怎样才能找出它拒绝访问的原因,更重要的是,我该如何解决它?

标签: javalinuxfilesystems

解决方案


我终于找到了问题的根本原因:它是由于文件权限,而不是错误消息提到的文件

我解压新应用程序更新的方法是用 Java 编写一个简单的“解压缩器”,它具有以下基本实现:

    try (var zip = new ZipInputStream(
            new BufferedInputStream(
                    new FileInputStream(newVersionZipFile), 4096))) {
        var zipEntry = zip.getNextEntry();
        if (zipEntry == null) {
            throw new IllegalStateException("Expected at least one entry in the zip file: " + newVersionZipFile);
        }
        var topEntryName = zipEntry.getName();
        zipEntry = zip.getNextEntry();
        while (zipEntry != null) {
            var file = fileFor(zipEntry, destinationDir, topEntryName);
            if (isDirectory(zipEntry)) {
                var ok = file.mkdir();
                if (!ok) throw new IllegalStateException("Cannot create new directory: " + file);
            } else {
                Files.copy(zip, file.toPath());
            }
            zipEntry = zip.getNextEntry();
        }
    }

这实际上大部分都有效,但它有一个大问题:它不考虑文件权限。我注意到了这个问题,并bin在解压后使目录中的所有内容都可以执行,但我没想到的是 jlink 创建的最小 JVM 中有更多的可执行文件。而且因为该应用程序实际上大部分工作,这让我觉得一切都很好!

使用该tree命令,我在使用 Linux 解压缩后查看了目录树unzip,并使用我的自定义 Java 解压缩。我注意到unzip,除了bin目录中的文件之外,以下文件还具有执行权限:

├── [drwxrwxr-x]  lib
│   ├── [-rw-rw-r--]  classlist
│   ├── [-rw-rw-r--]  javafx.properties
│   ├── [-rw-rw-r--]  javafx-swt.jar
│   ├── [-rwxrwxr-x]  jexec
│   ├── [-rw-rw-r--]  jrt-fs.jar
│   ├── [-rwxrwxr-x]  jspawnhelper
...

相比之下,Java 解压缩器解压缩的树看起来像这样:

├── [drwxrwxr-x]  lib
│   ├── [-rw-rw-r--]  classlist
│   ├── [-rw-rw-r--]  javafx.properties
│   ├── [-rw-rw-r--]  javafx-swt.jar
│   ├── [-rw-rw-r--]  jexec
│   ├── [-rw-rw-r--]  jrt-fs.jar
│   ├── [-rw-rw-r--]  jspawnhelper
...

注意到两个文件应该具有执行权限:jexecjspawnhelper,这两个文件的名称都强烈暗示它们与执行其他进程有关:D

为了验证这个假设,我让解压缩器也将这两个文件的权限更改为可执行,现在一切正常!

我的挑战现在转向如何在解压缩文件时实际保留所有文件的文件权限,这是另一个兔子洞,因为没有从标准 zip 获取文件权限的标准方法(这就是为什么用于 zips 的 Java API 没有该功能)。有一些外部库大部分都可以工作,所以我最终可能会使用其中一个......但这是另一个话题。


推荐阅读