如何使用 p/invoke 在 Linux 上获取 UID 和 GID?
How to get UID and GID on Linux using p/invoke?
我需要在 Linux 机器上对服务安装程序进行一些简单的文件操作。
代码是.NET 5.0.
我当前的版本使用Process.Start()
执行shell命令来更改文件和目录的所有者并设置权限。
这很慢(我使用异步方法),尤其是与 Windows 等效方法相比。
我看到可从 .NET 调用的 libc
具有方法 chmod
和 chown
,但是它需要 uid
和 gid
参数。我的应用程序不知道这些 ID,至少在不使用 shell 的情况下是这样。
到目前为止我得到了这样的东西:
const string LIBC = "libc";
[DllImport(LIBC, SetLastError = true)]
private static extern int chmod(string path, uint mode);
[DllImport(LIBC, SetLastError = true)]
private static extern int chown(string path, int owner, int group);
那么...如何获得所需的 2 个整数?
更新
为什么有人认为这个问题(尤其是考虑到它的标题)是关于相似但不同事物的问题的重复。
我知道如何以多种方式更改 Linux 文件的所有者和权限。最简单的方法是使用 Linux shell。最快最简单的方法是使用 Mono.Posix.NETStandard
库,在内部调用 libc
。
我的具体问题是它是如何制作的?它是如何工作的?
更具体地说:
这是 getpwnam()
的 Linux 手册页:
https://man7.org/linux/man-pages/man3/getpwnam.3.html
如何使用 p/invoke 从 C# 调用它?我在许多示例中看到,当他们将 char*
替换为 string
时,它会以某种方式神奇地起作用。我创建了一个这样的结构:
public struct PASSWD {
public string pw_name; /* username */
public string pw_passwd; /* user password */
public int pw_uid; /* user ID */
public int pw_gid; /* group ID */
public string pw_gecos; /* user information */
public string pw_dir; /* home directory */
public string pw_shell; /* shell program */
};
...并尝试将其用作签名的 out
参数。
我没有收到任何错误,但它就是行不通。我得到的结构是空的。
因此,我们再次使用 C# 中的 Platform Invoke,我们正在调用 libc
并且我们希望从结构中获取结果。据我谷歌搜索 - 它不是 google-able。只有 Mono
源代码,它使用实现我需要的外部模块。我怀疑他们这样做是出于性能原因,也使用了一些特殊工具,因为在注释中表示代码已生成。
我的问题又是,如何使用 Linux 手册页定义为 C# 创建适当的方法签名,以便能够从 getpwnam()
.
中提取这 2 个整数
我也很好奇 .NET 本身是否已经存在类似的东西,但我想它不存在。
所以,我对 p/invoke 生疏了。我的问题是我忘记了,当本机函数 returns 指向结构的指针时,没有自动转换,我必须在签名中留下指针,所以:
[DllImport(LIBC, SetLastError = true)]
internal static extern IntPtr getgrnam(string name);
[DllImport(LIBC, SetLastError = true)]
internal static extern IntPtr getpwnam(string name);
internal struct Group {
public string Name;
public string Password;
public uint Gid;
public IntPtr Members;
}
internal struct Passwd {
public string Name;
public string Password;
public uint Uid;
public uint Gid;
public string GECOS;
public string Directory;
public string Shell;
}
让我们创建完全托管的 .NET 样式类型:
public sealed class GroupInfo {
public string Name { get; }
public uint Id { get; }
public string[] Members { get; }
internal GroupInfo(Syscall.Group group) {
Name = group.Name;
Id = group.Gid;
Members = GetMembers(group.Members).ToArray();
}
private static IEnumerable<string> GetMembers(IntPtr members) {
IntPtr p;
for (int i = 0; (p = Marshal.ReadIntPtr(members, i * IntPtr.Size)) != IntPtr.Zero; i++)
yield return Marshal.PtrToStringAnsi(p)!;
}
}
public class UserInfo {
public string Name { get; }
public uint Uid { get; }
public uint Gid { get; }
public string? Directory { get; }
public string? Shell { get; }
internal UserInfo(Syscall.Passwd passwd) {
Name = passwd.Name;
Uid = passwd.Uid;
Gid = passwd.Gid;
Directory = passwd.Directory;
Shell = passwd.Shell;
}
}
并且可以这样使用:
public static UserInfo? GetUserInfo(string name) {
var result = Syscall.getpwnam(name);
if (result != IntPtr.Zero) return new UserInfo(Marshal.PtrToStructure<Syscall.Passwd>(result));
return null;
}
public static GroupInfo? GetGroupInfo(string name) {
var result = Syscall.getgrnam(name);
if (result != IntPtr.Zero) return new GroupInfo(Marshal.PtrToStructure<Syscall.Group>(result));
return null;
}
我需要在 Linux 机器上对服务安装程序进行一些简单的文件操作。 代码是.NET 5.0.
我当前的版本使用Process.Start()
执行shell命令来更改文件和目录的所有者并设置权限。
这很慢(我使用异步方法),尤其是与 Windows 等效方法相比。
我看到可从 .NET 调用的 libc
具有方法 chmod
和 chown
,但是它需要 uid
和 gid
参数。我的应用程序不知道这些 ID,至少在不使用 shell 的情况下是这样。
到目前为止我得到了这样的东西:
const string LIBC = "libc";
[DllImport(LIBC, SetLastError = true)]
private static extern int chmod(string path, uint mode);
[DllImport(LIBC, SetLastError = true)]
private static extern int chown(string path, int owner, int group);
那么...如何获得所需的 2 个整数?
更新
为什么有人认为这个问题(尤其是考虑到它的标题)是关于相似但不同事物的问题的重复。
我知道如何以多种方式更改 Linux 文件的所有者和权限。最简单的方法是使用 Linux shell。最快最简单的方法是使用 Mono.Posix.NETStandard
库,在内部调用 libc
。
我的具体问题是它是如何制作的?它是如何工作的?
更具体地说:
这是 getpwnam()
的 Linux 手册页:
https://man7.org/linux/man-pages/man3/getpwnam.3.html
如何使用 p/invoke 从 C# 调用它?我在许多示例中看到,当他们将 char*
替换为 string
时,它会以某种方式神奇地起作用。我创建了一个这样的结构:
public struct PASSWD {
public string pw_name; /* username */
public string pw_passwd; /* user password */
public int pw_uid; /* user ID */
public int pw_gid; /* group ID */
public string pw_gecos; /* user information */
public string pw_dir; /* home directory */
public string pw_shell; /* shell program */
};
...并尝试将其用作签名的 out
参数。
我没有收到任何错误,但它就是行不通。我得到的结构是空的。
因此,我们再次使用 C# 中的 Platform Invoke,我们正在调用 libc
并且我们希望从结构中获取结果。据我谷歌搜索 - 它不是 google-able。只有 Mono
源代码,它使用实现我需要的外部模块。我怀疑他们这样做是出于性能原因,也使用了一些特殊工具,因为在注释中表示代码已生成。
我的问题又是,如何使用 Linux 手册页定义为 C# 创建适当的方法签名,以便能够从 getpwnam()
.
我也很好奇 .NET 本身是否已经存在类似的东西,但我想它不存在。
所以,我对 p/invoke 生疏了。我的问题是我忘记了,当本机函数 returns 指向结构的指针时,没有自动转换,我必须在签名中留下指针,所以:
[DllImport(LIBC, SetLastError = true)]
internal static extern IntPtr getgrnam(string name);
[DllImport(LIBC, SetLastError = true)]
internal static extern IntPtr getpwnam(string name);
internal struct Group {
public string Name;
public string Password;
public uint Gid;
public IntPtr Members;
}
internal struct Passwd {
public string Name;
public string Password;
public uint Uid;
public uint Gid;
public string GECOS;
public string Directory;
public string Shell;
}
让我们创建完全托管的 .NET 样式类型:
public sealed class GroupInfo {
public string Name { get; }
public uint Id { get; }
public string[] Members { get; }
internal GroupInfo(Syscall.Group group) {
Name = group.Name;
Id = group.Gid;
Members = GetMembers(group.Members).ToArray();
}
private static IEnumerable<string> GetMembers(IntPtr members) {
IntPtr p;
for (int i = 0; (p = Marshal.ReadIntPtr(members, i * IntPtr.Size)) != IntPtr.Zero; i++)
yield return Marshal.PtrToStringAnsi(p)!;
}
}
public class UserInfo {
public string Name { get; }
public uint Uid { get; }
public uint Gid { get; }
public string? Directory { get; }
public string? Shell { get; }
internal UserInfo(Syscall.Passwd passwd) {
Name = passwd.Name;
Uid = passwd.Uid;
Gid = passwd.Gid;
Directory = passwd.Directory;
Shell = passwd.Shell;
}
}
并且可以这样使用:
public static UserInfo? GetUserInfo(string name) {
var result = Syscall.getpwnam(name);
if (result != IntPtr.Zero) return new UserInfo(Marshal.PtrToStructure<Syscall.Passwd>(result));
return null;
}
public static GroupInfo? GetGroupInfo(string name) {
var result = Syscall.getgrnam(name);
if (result != IntPtr.Zero) return new GroupInfo(Marshal.PtrToStructure<Syscall.Group>(result));
return null;
}