我最近发现,我可以相对容易地从 .NET 进行 Linux 系统调用。
例如,看看我是否需要sudo
我只是做了这样的签名:
internal class Syscall {
[DllImport("libc", SetLastError = true)]
internal static extern uint geteuid();
// ...
}
public static bool IsRoot => Syscall.geteuid() == 0;
整洁的。比其他一切都更容易和更快,对吗?
这是最简单的系统调用,其他使用字符串和结构。
经过一番深入研究文档并亲自测试后,我发现来自的字符串libc
可以直接映射到string
from char*
默认情况下,编组器,大多数其他东西只需要使用一些手动映射的乐趣IntPtr
到结构。
所以以类似的方式我快速映射chmod
, chown
, lchown
, getgrnam
, getpwnam
, getuid
, symlink
。所有这些都在我的 Ubuntu VM 上进行了测试,有效。
我什至自己做了超级整洁Chmod
与 shell 工作方式相同的实现chmod
接受相对权限,例如u+wX
。并遍历文件系统。
这就是我失去一个晚上的地方。我需要原始权限,我读到可以通过以下方式获得它们stat
称呼。可能会出现什么问题?
首先我做了Stat
结构使用Linux
手册文档:https://man7.org/linux/man-pages/man2/stat.2.html
然后我做了相应的extern
.
第一个惊喜:找不到入口点。
我挖啊挖啊,又挖了一些。直到我刚刚打开我的libc
二进制并搜索类似的东西stat
。答对了!我发现__xstat
观点。就是这样,我更改了我的签名,我在文档中阅读了除了指定之外的内容ver
参数(应设置为3
) - 它应该有效完全相同的 to stat
.
事实并非如此。调用通过,但是总是返回-1,不返回Stat
结构。
然后我找到了一些资源__xstat
它检查是否ver
参数与内核版本匹配。诡异的!但我尝试通过5
。因为这是我当前使用的内核版本。还有一些其他数字,例如“3”和“0”。没有运气。什么都不起作用。我也测试过__xstat64
。同样的结果,我的意思是没有结果。
然后我在 GitHub 上发现了一个讨论.NET
开发人员,那个召唤stat
非常棘手,因为每个内核都不同。等待,WHAT!?
是的,我知道它在Mono.Posix.NETStandard 1.0.0
包,我使用它并且它有效。 (这就是人们推荐的。)
但由于我刚刚学习平台调用“voodoo” - 我不能就这样离开它。为什么除了stat
调用没有任何问题,为什么会出现异常?这是一个完全基本的事情。在“为什么”之后是“如何?”。
他们做到了Mono
。我挖了进去Mono
GitHub 上的资源发现,它是少数几个实际上没有调用的函数之一libc
但来自他们自己在 C 中的汇编:https://github.com/mono/mono/blob/main/support/sys-stat.c
很有趣,但我仍然很难理解它是如何工作的。
顺便说一句,添加Mono
我的项目将编译后的可执行 Linux x64 文件从 200kb 增加到 1200kb。字面上增加1个读取单个数字的功能!顺便说一句,它有一个许可证问题,包签名说MIT
,链接的源文件说MPL
。我的软件包要求用户接受这个奇怪的许可证。我的意思是,接受MIT
,虽然我不太确定这是否真的MIT
or MPL
。我自己的包使用MIT
.
那么 - 打电话时有哪些(其他)问题和陷阱libc
来自点网?有没有更简单的调用方式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);
}
作品。它只需要 1MB 多;)我知道,这没什么,但我只是为了 1 个简单的函数而有外部依赖。
根据我的研究 -Stat
结构因内核而异。我怀疑如果我尝试其他一些版本,其中一个最终会工作,但它根本不能解决问题,因为它可以在目标计算机上更新后停止工作。
我的猜测是,当 Linux 中需要并允许更改结构时,必须有一种通用接口/兼容性机制,允许用户在不详细了解特定目标机器上的系统和库版本的情况下获得权限。
我想libc
只是类似的东西,但似乎要么不完全是这样,要么 Linux 中的其他地方有更高级别的接口,我在这里指的不是 shell ;)
我主要有Windows背景,我经常使用Windows p/invoke。我为 Windows 7 编写的大部分代码仍然可以在 Windows 11 上运行。旧Win32
调用没有改变,除了一些非常系统 UI 特定的调用。