[英]How to get Linux file permissions in .NET 5 / .NET 6 without Mono.Posix with p/invoke?
我最近发现,我可以相对容易地从 .NET 进行 Linux 系统调用。
例如,要查看我是否需要sudo
,我只需像这样签名:
internal class Syscall {
[DllImport("libc", SetLastError = true)]
internal static extern uint geteuid();
// ...
}
public static bool IsRoot => Syscall.geteuid() == 0;
整洁的。 比其他任何事情都容易和快得多,对吧? 这是最简单的系统调用,其他使用字符串和结构。
在对文档进行一些挖掘并为自己进行测试之后,我发现默认编组器可以将libc
中的string
直接映射到char*
中的字符串,大多数其他内容只需要使用一些有趣的方法将IntPtr
手动映射到结构。
因此,我以类似的方式快速映射了chmod
、 chown
、 lchown
、 getgrnam
、 getpwnam
、 getuid
、 symlink
。 全部在我的 Ubuntu VM 上测试,有效。
我什至制作了我自己的超级简洁的Chmod
实现,它与 shell chmod
的工作方式相同,它接受像u+wX
这样的相对权限。 并遍历文件系统。
那就是我失去一个晚上的地方。 我需要原始权限,我读到它们可以通过stat
调用获得。 go 可能有什么错误?
首先,我使用Linux
手册文档制作了Stat
结构: https://man7.org/linux/man-pages/man2/stat.2.html
然后我做了适当的extern
。
第一个惊喜:找不到入口点。
我挖了又挖又挖了一些。 直到我打开我的libc
二进制文件并搜索类似于stat
的东西。 答对了! 我找到__xstat
点。 就是这样,我更改了我的签名,我在文档中读到除了指定ver
参数(应该设置为3
)之外 - 它应该与stat
相同。
它没有。 调用通过,但总是返回-1,不返回Stat
结构。
然后我找到了__xstat
的一些来源,它检查ver
参数是否与 kernel 版本匹配。 诡异的! 但我试着通过5
。 因为它是我当前使用的 kernel 版本。 还有一些其他数字,如“3”和“0”。 没有运气。 什么都不管用。 我还测试__xstat64
。 同样的结果,我的意思是没有结果。
然后我发现.NET
开发人员之间关于 GitHub 的讨论,调用stat
非常棘手,因为它在每个 kernel 上都不一样。等等,什么??
是的,我知道它在Mono.Posix.NETStandard 1.0.0
package 中,我使用它并且有效。 (这就是那些人推荐的。)
但由于我只是在学习平台调用“voodoo”——我不能就这样离开它。 为什么除了stat
调用之外的一切都没有任何问题,为什么会有例外? 这是一个完全基本的东西。 然后在“为什么”之后是“如何?”。
他们在Mono
中做到了。 我在 GitHub 上挖掘了Mono
的来源,发现它是少数几个 function 之一,实际上不是从libc
调用的,而是从 C 中他们自己的程序集调用的: https://github.com/mono/mono/blob/main/support/sys-状态 c
有趣,但我仍然很难理解它是如何工作的。
顺便说一句,将Mono
添加到我的项目中会使我编译的可执行文件 Linux x64 文件从 200kb 增加到 1200kb。 要从字面上添加 1 function 读取单个数字,顺便说一句,它有一个许可证问题, package 签名说MIT
,链接的源文件说MPL
。 我的 package 要求用户接受这个奇怪的许可证。 我的意思是,接受MIT
,虽然我不太确定它是真的MIT
还是MPL
。 我自己的package用的是MIT
。
那么 - 从 do.net 调用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 个简单的 function。
根据我的研究 - Stat
结构从 kernel 到 kernel 不同。我怀疑如果我尝试了一些其他版本,一个最终会工作,但它根本没有解决问题,因为它可以在目标机器上更新后停止工作.
我的猜测是当 Linux 中需要并允许更改结构时,必须有一种通用接口/兼容性机制允许用户在不详细了解特定目标机器上的系统和库版本的情况下获得权限。
我认为libc
就是这样的东西,但它似乎不完全是它,或者在 Linux 中的其他地方有一个更高级别的接口,我在这里不是指 shell;)
我主要有 Windows 背景,我经常使用 Windows p/invoke。 我为 Windows 7 编写的大部分代码仍然适用于 Windows 11。旧的Win32
调用没有改变,除了一些非常系统 UI 特定的调用。
所以,我终于找到了: https : //github.com/tmds/Tmds.LibC/blob/master/src/Sources/linux.common/stat.cs
这是最小的工作示例:
系统调用.cs:
using System;
using System.Runtime.InteropServices;
namespace LinuxStat;
internal class Syscall {
/// <summary>
/// Returns file mode (permissions).
/// </summary>
/// <param name="path">Path.</param>
/// <returns>File mode (permissions).</returns>
/// <exception cref="InvalidOperationException">Stat system call fails.</exception>
public static uint GetFileMode(string path) {
var ver = RuntimeInformation.OSArchitecture switch {
Architecture.X64 => 1,
Architecture.Arm64 => 1,
_ => 3
};
if (__xstat(ver, path, out var data) != 0) throw new InvalidOperationException($"Stat failed for {path}.");
return data.Mode;
}
/// <summary>
/// 108 bytes fixed structure with file mode at offset 0x0a.
/// </summary>
internal unsafe struct stat_file_mode { // 108 bytes
/// <summary>
/// Reserved.
/// </summary>
private fixed int _reserved1[6];
/// <summary>
/// File permissions data.
/// </summary>
public uint Mode;
/// <summary>
/// Reserved.
/// </summary>
private fixed int _reserved2[47];
}
[DllImport("c", SetLastError = true)]
internal static extern int __xstat(int ver, string path, out stat_file_mode data);
}
并在Program.cs
测试:
using System;
using System.Runtime.InteropServices;
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
Console.WriteLine("Linux please.");
Environment.Exit(-1);
}
var target = "stat-test";
var mode = LinuxStat.Syscall.GetFileMode(target);
Console.WriteLine($"File mode for {target}: {Convert.ToString(mode, 8)}.");
结果:
codedog@codedog-vm:~/uploads$ ./stat-test
File mode for stat-test: 100774.
我用ox
对它进行了修改以进行测试。
尽管搜索了 16 个小时,但我还是找不到正确的结构定义。 最后,我只是将所有缓冲区转储为八进制,并找到了预期权限的偏移量。 作品? 作品。 大功告成。
仅作记录,fxstatat() 和 statx() 结构在 MAC 上没有给我正确的文件权限值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.