Например, чтобы узнать, нужен ли мне 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
}
Конечно
Код: Выделить всё
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);
}
Из того, что я исследовал - структура Stat отличается от ядра к ядру. Я подозреваю, что если бы я попробовал некоторые другие версии, одна из них, наконец, сработала бы, но это не решает проблему вообще, потому что она может перестать работать после обновления на целевой машине.
Я предполагаю, что когда структура требуется и ее можно изменять в Linux, должен быть своего рода общий механизм интерфейса/совместимости, позволяющий пользователям получать разрешения без подробных знаний о версиях системы и библиотеки на конкретной целевой машине.
Я думал, что libc - это что-то вроде этого, но похоже, что либо это не совсем так, или где-то еще в Linux есть интерфейс немного более высокого уровня, и я здесь не имею в виду оболочку
У меня в основном фон Windows, я часто использовал Windows p/invoke. Большая часть кода, который я написал для Windows 7, по-прежнему работает в Windows 11. Старые вызовы Win32 не изменились, за исключением некоторых, очень специфичных для системного пользовательского интерфейса.
Подробнее здесь: https://stackoverflow.com/questions/697 ... x-with-p-i
Мобильная версия