簡體   English   中英

為什么 Linux 不執行具有 Java 執行權限的 bash 腳本?

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

有許多看起來與此類似的問題,但似乎沒有一個問題完全相同,而且他們的所有解決方案都對我不起作用,所以這里......

我有一個嘗試自動更新的 Java 程序。 它是這樣工作的:

  • 啟動時,bash 腳本會檢查某個位置是否存在 zip 文件。
  • 如果存在,它不會運行“真正的”主 class,而是運行更新程序 Java class。
  • 此 class 將在臨時位置解壓縮 zip,然后在單獨的進程中從該臨時位置重新啟動。
  • 一旦新進程開始,舊進程就會退出。
  • 新進程嘗試再次將 zip 解包到之前的位置(現在沒有任何運行,因此可以更換)。
  • 成功后,它會刪除 zip 文件並再次運行原始 bash 腳本。

如果一切正常,bash 腳本現在將啟動 Java 主 class。

此列表中的所有內容實際上都有效,除了最后一步我嘗試在最后執行 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)

我寫了一個類似的“過程炸彈”,但它有效:

  • 首先運行啟動主 class 的 bash 腳本
  • 主 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(); }

所以我認為這不是針對此類過程炸彈的某種 Linux 保護。

但我完全不明白為什么 Linux 在更新后仍然拒絕訪問運行該 bash 腳本。

我的操作系統是 Ubuntu 19。

我怎樣才能找出它拒絕訪問的原因,更重要的是,我該如何解決它?

我終於找到了問題的根本原因:它是由於文件權限,而不是錯誤消息提到的文件

我解壓新應用程序更新的方式是通過在 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 獲取文件權限(這就是為什么 Java ZDB974238714CA8DE634A 沒有該功能)。 有一些外部庫大部分都可以工作,所以我最終可能會使用其中一個......但這是另一個話題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM