Получить путь от FileDescriptor в Java
Краткий обзор моей проблемы:
Я пишу компилятор, который преобразует спецификации доменного типа в код Java Security Manager. В двух словах, DTE определяет "типы" (объекты), назначает пути к этим типам; затем определяет "домены" (субъекты) и определяет, какие домены разрешений (rwxdc) имеют для различных типов. Мне нужно подражать этому поведению как можно ближе в JSM.
В настоящее время я работаю над разрешениями на запись. Я успешно переопределил метод checkWrite(String filename) в JSM. Следующим в списке является checkWrite(FileDescriptor filedescriptor), и этот оказывается более хитрым. Из-за способа определения DTE мне нужна информация о пути, чтобы определить, допустимо ли действие записи.
Можно ли извлечь данные пути из FileDescriptor? Я предполагаю, что нет - я проверил документацию и различные учебные пособия, и я не нашел ничего, что указывало бы на то, что у меня есть какой-либо способ получить эту информацию (однако я был бы рад, если бы мне показывали неправильно; сделай мою работу проще).
Если ответ на вышеизложенный ответ НЕТ, может ли кто-нибудь предложить жизнеспособный обходной путь? Например, есть ли какой-нибудь способ, которым я мог бы написать собственный код, чтобы делать то, что я хочу, и связать это с моим пользовательским классом JSM? Я в порядке, когда делаю что-то "продвинутое", но мне нужно кое-что понять, как начать.
Или мой единственный вариант - вообще запретить все права на запись, использующие FileDescriptor? Я бы очень хотел избежать этого, потому что это грязное решение, но если это реальность, мне нужно знать.
Большое спасибо за ваше время.
4 ответа
Краткий ответ - нет, поскольку файл не зависит от пути, используемого для доступа к этому файлу (по крайней мере, в любой ОС, которая имеет значение).
Один из возможных обходных путей - перехватывать вызовы, открывающие файлы, с использованием аспектной структуры и помещать файловые дескрипторы, на которые ссылаются, в WeakHashMap<FileDescriptor,File>
, Затем вы просто смотрите на эту карту всякий раз, когда вам нужно подтвердить запись.
Надеюсь, это не будет заблокировано ограничениями отражения, но пока вы можете использовать это (код, немного основанный на другом ответе), начиная с Android O:
val file = File(filesDir, "ff")
file.parentFile!!.mkdirs()
val fileOutputStream = FileOutputStream(file)
val fd = fileOutputStream.fd
val method = fd.javaClass.getMethod("getInt$")
val fdId = method.invoke(fd)
val path = Paths.get("/proc/self/fd/$fdId")
val filePath = Files.readSymbolicLink(path)
Log.d("AppLog", "filePath:$filePath")
Не уверен насчет FileChannel. Его файл реализации ("FileChannelImpl") должен иметь "путь" (путь к файлу типа String) и "fd", который является дескриптором FileDescriptor, но оба они скрыты посредством отражения.
Решение для получения пути от FileDescriptor в java:
Как это устроено:
Мы знаем, что файловый дескриптор содержит идентификатор дескриптора для поиска открытого файла в текущем процессе.
Что такое файловые дескрипторы, простыми словами?
если мы знаем идентификатор дескриптора, мы можем легко найти путь к файлу, выполнив java-код:
Path path = Paths.get("/proc/self/fd/"+fd_id);
System.out.println(Files.readSymbolicLink(path)); //return file path in file descriptor
Вот:
fd_id идентификатор файлового дескриптора (0,1,2.....)
/ proc его каталог содержит все процессы, запущенные в системе
/ самостоятельно запущенный в данный момент идентификатор процесса класса Java
/fd каталог файловых дескрипторов
//fd_id идентификатор файлового дескриптора
SafeFileDescriptor.java
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.file.Path;
public class SafeFileDescriptor {
static {
System.load("Documents/java native interface exmples/libSafeFileDescriptor.so");
}
private native int getFDid(FileDescriptor fd);
public static void main(String[] args) throws IOException{
FileOutputStream fout = new FileOutputStream("Documents/test.txt");
FileDescriptor fd=fout.getFD();
int fd_id = new SafeFileDescriptor().getFDid(fd);
Path path = Paths.get("/proc/self/fd/"+fd_id);
System.out.println(Files.readSymbolicLink(path));
}
}
getFDid()
- это собственный метод, используемый для получения идентификатора дескриптора данного объекта дескриптора файла
следующий код является реализацией getFDid()
родной метод
SafeFileDescriptor.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class SafeFileDescriptor */
#ifndef _Included_SafeFileDescriptor
#define _Included_SafeFileDescriptor
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: SafeFileDescriptor
* Method: getFDid
* Signature: (Ljava/io/FileDescriptor;)I
*/
JNIEXPORT jint JNICALL Java_SafeFileDescriptor_getFDid
(JNIEnv *, jobject, jobject);
#ifdef __cplusplus
}
#endif
#endif
создать SafeFileDescriptor.h из java-файла SafeFileDescriptor.java
javac -h dir SafeFileDescriptor.java
замените "dir" на ваш каталог, чтобы сохранить SafeFileDescriptor.h
SafeFileDescriptor.c
#include <jni.h>
#include "SafeFileDescriptor.h"
JNIEXPORT jint JNICALL Java_SafeFileDescriptor_getFDid
(JNIEnv *env, jobject this_object, jobject fdObject) {
jclass fileDescriptor = (*env)->GetObjectClass(env,fdObject);
jfieldID id_fd = (*env)->GetFieldID(env, fileDescriptor, "fd", "I");
return (*env)->GetIntField(env,fdObject,id_fd);
}
скомпилировать SafeFileDescriptor.c
gcc -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libSafeFileDescriptor.so SafeFileDescriptor.c
Чтобы добавить файл libSafeFileDescriptor.so в файл класса Java
System.load("Documents/java native interface exmples/libSafeFileDescriptor.so");
Добавлена дополнительная информация, основанная на ответе разработчика @android и @zakmck.
/dev/fd — лучшее решение для большинства вариантов Unix, включая большинство вариантов Linux и MacOS, но не для всех. У меня это работает на CentOS 9 и MacOS 12.1.
fdesc Файловая система fdesc обычно монтируется в /dev/fd. Его функциональность аналогична /proc//fd (или просто /proc/self/fd) в Linux, то есть он предоставляет список всех активных файловых дескрипторов для текущего запущенного процесса. Обратите внимание, что в типичной системе Linux /dev/fd символически связан с /proc/self/fd.
Использованная литература: