[英]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.