Как получить права доступа к файлам Linux в .NET 5/.NET 6 без Mono.Posix с помощью p/invoke?C#

Место общения программистов C#
Ответить
Anonymous
 Как получить права доступа к файлам Linux в .NET 5/.NET 6 без Mono.Posix с помощью p/invoke?

Сообщение Anonymous »

Недавно я обнаружил, что могу относительно легко выполнять системные вызовы Linux из .NET.
Например, чтобы узнать, нужен ли мне sudo, я просто делаю такую ​​подпись:

Код: Выделить всё

internal class Syscall {

[DllImport("libc", SetLastError = true)]
internal static extern uint geteuid();

// ...
}

Код: Выделить всё

public static bool IsRoot => Syscall.geteuid() == 0;
Аккуратно. Намного проще и быстрее, чем все остальное, не так ли?
Это самый простой системный вызов, другие используют строки и структуры.
После некоторого копания в документации и тестирования на себе я обнаружил, что строки из libc могут быть сопоставлены непосредственно со строками из char* с помощью маршаллера по умолчанию, большинство других вещей требуют просто немного поразвлечься с ручным сопоставлением IntPtr со структурами.
Таким же образом я быстро сопоставил chmod, chown, lchown, getgrnam, getpwnam, getuid, symlink. Все проверено на моей виртуальной машине Ubuntu, работает.
Я даже сделал свою собственную супер аккуратную реализацию Chmod, которая работает идентично chmod оболочки, которая принимает относительные разрешения, такие как u+wX. И ходит по файловой системе.
И вот тут я ночь потерял. Мне нужны были оригинальные разрешения, и я читал, что их можно получить с помощью вызова stat. Что может пойти не так?
Сначала я создал структуру Stat, используя справочную документацию Linux:
https://man7.org/linux/man-pages/man2/stat.2.html
Затем я сделал соответствующий extern.
Первый сюрприз: точка входа не найдена.
Я копал, копал и накопал еще. Пока я не открыл двоичный файл libc и не начал искать что-то похожее на stat. Бинго! Я нашел точку __xstat. Вот и все, я изменил свою подпись, прочитал в документации, что помимо указания параметра ver (который должен быть установлен на 3) - он должен работать идентично stat.
Это не так. Вызов проходит, но он всегда возвращает -1, не возвращает структуру Stat.
Затем я нашел несколько источников __xstat, где он проверяет, соответствует ли параметр ver версии ядра. СТРАННЫЙ! Но я попробовал передать 5. Потому что я использую текущую версию ядра. Также некоторые другие цифры, такие как «3» и «0». Не повезло. Ничего не работает. Я также протестировал __xstat64. Результат тот же, я имею в виду отсутствие результата.
Затем я нашел на GitHub дискуссию между разработчиками .NET о том, что вызов stat очень сложен, потому что в каждом ядре он разный. Подождите, ЧТО!?
Да, я знаю, что это есть в пакете Mono.Posix.NETStandard 1.0.0, я использую его, и он работает. (И это то, что ребята рекомендовали.)
Но поскольку я только учусь платформерному вызову "вуду" - я просто не могу оставить это так. Почему все, кроме вызова stat, работает без проблем, почему есть исключение? Это совершенно БАЗОВАЯ вещь. Затем после «почему» следует «КАК?».
Они сделали это в Mono. Я покопался в исходниках Mono на GitHub и обнаружил, что это одна из немногих функций, которая на самом деле вызывается не из libc, а из их собственной сборки в C:
https://github.com/mono/mono/blob/main/ ... sys-stat.c
Интересно, но мне все еще трудно понять, как она работает.
Кстати, добавление Mono в мой проект увеличилось мой скомпилированный исполняемый файл Linux x64 размером от 200 КБ до 1200 КБ. Добавить буквально 1 функцию чтения одного числа! Кстати, у него проблема с лицензией, в подписи пакета указано MIT, в исходном файле указано MPL. И мой пакет предлагает пользователям принять эту любопытную лицензию. Я имею в виду принять MIT, хотя я не совсем уверен, действительно ли это MIT или MPL. Мой собственный пакет использует MIT.
Итак, какие (другие) подвохи и ошибки при вызове libc из dotnet? Есть ли более простой способ вызвать stat()? Есть ли альтернативный способ получить разрешения от .NET? Я понял, что сама .NET ДЕЛАЕТ это внутри себя. Он получает права доступа к файлам, доступные из FileInfo. Однако атрибуты «переводятся» в структуру Windows, и большая часть информации теряется при переводе.
Моя последняя попытка:

Код: Выделить всё

[DllImport("libc", SetLastError = true)]
internal static extern int __xstat(int ver, string path, out Stat stat);

internal struct Stat {

public ulong st_dev;        // device
public ulong st_ino;        // inode
public uint st_mode;        // protection
public ulong st_nlink;      // number of hard links
public uint st_uid;         // user ID of owner
public uint st_gid;         // group ID of owner
public ulong st_rdev;       // device type (if inode device)
public long st_size;        // total size, in bytes
public long st_blksize;     // blocksize for filesystem I/O
public long st_blocks;      // number of blocks allocated
public long st_atime;       // time of last access
public long st_mtime;       // time of last modification
public long st_ctime;       // time of last status change
public long st_atime_nsec;  // Timespec.tv_nsec partner to st_atime
public long st_mtime_nsec;  // Timespec.tv_nsec partner to st_mtime
public long st_ctime_nsec;  // Timespec.tv_nsec partner to st_ctime

}
Вызывается как Syscall.__xstat(5, path, out Stat stat). Возвращает -1 для любого пути, который я пробовал.
Конечно

Код: Выделить всё

public static Permissions GetPermissions(string path) {
if (Mono.Unix.Native.Syscall.stat(path, out var stat) != 0) throw new InvalidOperationException($"Stat call failed for {path}");
return new Permissions((uint)stat.st_mode);
}
работает. Это займет всего на 1 МБ больше ;) Я знаю, это ничего, но у меня есть внешняя зависимость только для 1 простой функции.
Из того, что я исследовал - структура Stat отличается от ядра к ядру. Я подозреваю, что если бы я попробовал некоторые другие версии, одна из них, наконец, сработала бы, но это не решает проблему вообще, потому что она может перестать работать после обновления на целевой машине.
Я предполагаю, что когда структура требуется и ее можно изменять в Linux, должен быть своего рода общий механизм интерфейса/совместимости, позволяющий пользователям получать разрешения без подробных знаний о версиях системы и библиотеки на конкретной целевой машине.
Я думал, что libc - это что-то вроде этого, но похоже, что либо это не совсем так, или где-то еще в Linux есть интерфейс немного более высокого уровня, и я здесь не имею в виду оболочку ;)
У меня в основном фон Windows, я часто использовал Windows p/invoke. Большая часть кода, который я написал для Windows 7, по-прежнему работает в Windows 11. Старые вызовы Win32 не изменились, за исключением некоторых, очень специфичных для системного пользовательского интерфейса.

Подробнее здесь: https://stackoverflow.com/questions/697 ... x-with-p-i
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»