Почему Linux не выполняет bash сценарий, который имеет разрешение на выполнение от Java? - PullRequest
1 голос
/ 13 апреля 2020

Есть множество вопросов, которые похожи на это, но ни один из них не выглядит точно так же, и все их решения не сработали для меня, так что вот так ...

У меня есть Java программа, которая пытается самообновиться. Это работает так:

  • при запуске, скрипт bash проверяет наличие zip-файла в определенном месте.
  • , если он существует, вместо запуска " настоящий "основной класс, он запускает класс обновлений Java.
  • этот класс распакует zip-архив во временную папку, а затем снова запустит себя в отдельном процессе из этой временной папки.
  • как только начинается новый процесс, старый выходит из системы.
  • новый процесс пытается снова распаковать zip-файл в прежнее место (в котором сейчас ничего не работает, поэтому его можно заменить).
  • в случае успеха он удаляет zip-файл и снова запускает оригинальный сценарий bash.

Если все работает, сценарий 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)

Я написал «бомбу процесса», которая работает аналогично, но она работала:

  • начните с запуска сценария bash, который запускает основной класс
  • основной класс использует следующий код для повторного запуска сценария 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.

Как я могу узнать, почему он запрещает доступ, и, что более важно, как я могу это исправить?

1 Ответ

1 голос
/ 19 апреля 2020

Я наконец выяснил причину проблемы root: это было связано с правами доступа к файлу, но не с файлом, в сообщении об ошибке упоминается !

То, как я был распаковка нового обновления приложения осуществлялась путем написания простой "unzipper" в Java со следующей базовой реализацией c:

    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 исполняемым после распаковки, но я не ожидал, что в минимальной JVM будет больше исполняемых файлов, созданных jlink. И поскольку приложение на самом деле в основном работало, это заставило меня думать, что все в порядке!

Используя команду tree, я посмотрел на дерево каталогов после распаковки его с помощью Linux s 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 unzipper, выглядело как this:

├── [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
...

Заметил, что два файла должны иметь разрешение на выполнение: jexec и jspawnhelper, оба из которых имеют имена, которые явно указывают на то, что они как-то связаны с выполнением других процессов: D

Чтобы проверить эту гипотезу, я заставил расстегнуть молнию и изменить разрешения этих двух файлов на исполняемые, и теперь все работает как положено!

Теперь моя задача заключается в том, как на самом деле сохранить права доступа к файлам. всех файлов при разархивировании, и это еще одна кроличья нора, поскольку нет стандартного способа получить права доступа к файлам из стандартного почтового индекса (именно поэтому API Java для почтовых индексов не имеет этой функции). Существуют внешние библиотеки, которые в основном работают, поэтому я, вероятно, в конечном итоге буду использовать одну из них ... но это еще одна топика c.

...