如何在Linux上使用p/invoke获取UID和GID ?



我需要在Linux机器上进行一些简单的文件操作,用于服务安装程序。代码是。net 5.0。

我的当前版本使用Process.Start()执行shell命令来更改文件和目录的所有者并设置权限。

这是相当慢的(我使用异步方法),特别是与Windows等效方法相比。

我看到libc可以从。net调用chmodchown方法,但是它需要uidgid参数。我的应用程序不知道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#调用它?我在许多例子中看到,当他们用string代替char*时,它不知何故神奇地起作用了。我创建了一个这样的结构体:

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#中,我们调用libc,我们想从结构中获得结果。就我在谷歌上搜索的结果而言,这是无法用谷歌搜索的。只有Mono源代码,它使用实现我需要的外部模块。我怀疑他们这么做也是出于性能的考虑——使用了一些特殊的工具,因为在注释中表示代码是生成的。

我的问题是,如何使用Linux手册页定义为c#创建适当的方法签名,以便能够从getpwnam()中提取这2个整数。

我也很好奇。net本身是否已经存在这样的东西,但我猜它没有。

所以,我对p/invoke有点生疏了。我的问题是我忘记了,当本机函数返回指向结构的指针时,没有自动转换,我必须在签名中留下指针,所以:

[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;
}

最新更新