简体   繁体   English

为什么 Linux 不执行具有 Java 执行权限的 bash 脚本?

[英]Why won't Linux execute a bash script that has execution permission from Java?

There are numerous questions that look similar to this, but none of them seem to be exactly the same and all their solutions have not worked for me, so here it goes...有许多看起来与此类似的问题,但似乎没有一个问题完全相同,而且他们的所有解决方案都对我不起作用,所以这里......

I have a Java program that tries to auto-update itself.我有一个尝试自动更新的 Java 程序。 It works like this:它是这样工作的:

  • on startup, a bash script checks for the presence of a zip file in a certain location.启动时,bash 脚本会检查某个位置是否存在 zip 文件。
  • if it exists, instead of running the "real" main class, it runs the updater Java class.如果存在,它不会运行“真正的”主 class,而是运行更新程序 Java class。
  • this class will unpack the zip in a temp location, then start itself again in a separate process, from that temp location.此 class 将在临时位置解压缩 zip,然后在单独的进程中从该临时位置重新启动。
  • once the new process starts, the old one exits.一旦新进程开始,旧进程就会退出。
  • the new process tries to unpack the zip again, into the previous location (which now has nothing running so can be replaced).新进程尝试再次将 zip 解包到之前的位置(现在没有任何运行,因此可以更换)。
  • on success, it deletes the zip file and runs the original bash script again.成功后,它会删除 zip 文件并再次运行原始 bash 脚本。

If everything worked, the bash script now would just start the Java main class.如果一切正常,bash 脚本现在将启动 Java 主 class。

Everything in this list actually works, except for the very last step when I try to execute the bash script at the end.此列表中的所有内容实际上都有效,除了最后一步我尝试在最后执行 bash 脚本。

The bash file permissions look like this (both before and after I run the auto-updater): bash 文件权限如下所示(在我运行自动更新程序之前和之后):

-rwxr-xr-x

As far as I can see, there should be no permissions issue running this file (and in fact, I can run it manually immediately after the process crashes).据我所知,运行这个文件应该没有权限问题(事实上,我可以在进程崩溃后立即手动运行它)。

Here's the error I get:这是我得到的错误:

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)

I wrote a "process bomb" that works similarly, but that worked:我写了一个类似的“过程炸弹”,但它有效:

  • start by running a bash script that launches a main class首先运行启动主 class 的 bash 脚本
  • the main class uses the following code to start the bash script again:主 class 使用以下代码再次启动 bash 脚本:

     // DO NOT RUN THIS AT HOME, // IT WILL START LOTS OF PROCESSES INFINITELY // if you do want to try it. have "killall -9 java" ready. try { Runtime.getRuntime();exec( path ) System.exit(0); } catch ( Exception e ) { e.printStackTrace(); }

So I think that this is not some kind of Linux protection against process bombs like this.所以我认为这不是针对此类过程炸弹的某种 Linux 保护。

But I am completely puzzled why Linux is still denying access to running that bash script after the update.但我完全不明白为什么 Linux 在更新后仍然拒绝访问运行该 bash 脚本。

My OS is Ubuntu 19.我的操作系统是 Ubuntu 19。

How can I find out why it's denying access, and more importantly, how can I fix it?我怎样才能找出它拒绝访问的原因,更重要的是,我该如何解决它?

I have finally found out the root cause of the problem: it was due to file permissions, but not with the file the error message mentions !我终于找到了问题的根本原因:它是由于文件权限,而不是错误消息提到的文件

The way I was unpacking the new application update was via writing a simple "unzipper" in Java with the following, basic implementation:我解压新应用程序更新的方式是通过在 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();
        }
    }

This actually works, mostly, but it has a big problem: it does not consider file permissions.这实际上大部分都有效,但它有一个大问题:它不考虑文件权限。 I had noticed the problem and made everything in the bin directory executable after unzipping, but what I had not anticipated was that there more executable files in the minimal-JVM created by jlink.我注意到了这个问题,解压后将bin目录中的所有内容都设置为可执行文件,但没想到 jlink 创建的最小 JVM 中有更多可执行文件。 And because the application actually mostly worked, that made me think everything was fine!而且因为该应用程序实际上大部分工作,这让我觉得一切都很好!

Using the tree command, I looked at the directory tree after unzipping it with Linux's unzip , and with my custom Java unzip.使用tree命令,我在使用 Linux 的unzip后查看了目录树,并使用我的自定义 Java 解压缩。 I noticed that, with unzip , besides the files in the bin directory, the following files also had execution permission:我注意到,使用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
...

In contrast, the tree unzipped by the Java unzipper looked like this:相比之下,由 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
...

Noticed that two files are supposed to have execution permission: jexec and jspawnhelper , both of which have names that strongly suggest they have something to do with executing other processes:D注意到两个文件应该具有执行权限: jexecjspawnhelper ,这两个文件的名称都强烈暗示它们与执行其他进程有关:D

To test this hypothesis, I made the unzipper also change the permissions of these two files to executable, and now everything works as expected!为了验证这个假设,我让解压缩器也将这两个文件的权限更改为可执行,现在一切正常!

My challenge now turns to how to actually keep the file permissions of all files when unzipping it, and that's another rabbit hole as there is no standard way to get file permissions from a standard zip (which is why the Java API for zips does not have that feature).我的挑战现在转向如何在解压缩文件时实际保留所有文件的文件权限,这是另一个兔子洞,因为没有标准方法可以从标准 zip 获取文件权限(这就是为什么 Java ZDB974238714CA8DE634A 没有该功能)。 There are external libraries that mostly work, so I'll probably end up using one of them... but that's another topic.有一些外部库大部分都可以工作,所以我最终可能会使用其中一个......但这是另一个话题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM