C#中如何创建归属指定用户组(而非默认组)的目录?
问题描述
我正在编写一个程序,计划以守护进程/服务/无登录用户aaa运行。用户aaa属于shareabc和sharedef组。程序的配置文件如下:
{ ... other config ... "locations": [ { "id": "workflow1", "location": "/shr/abc", "group": "shareabc" }, { "id": "workflow2", "location": "/shr/def", "group": "sharedef" }, ], ... other config ... }
相关目录归属情况:
/shr/abc归属为root:shareabc/shr/def归属为root:sharedef
调用Directory.CreateDirectory("/shr/abc/myfolder1");创建的/shr/abc/myfolder1会归属aaa:aaa,需要编写C#代码让该目录归属为aaa:shareabc。
排除方案说明:
将程序以用户aaa、组shareabc运行并非解决方案,原因如下:
- 部分文件需归属
aaa:aaa,另一部分需归属aaa:shareabc,修改运行用户/组或文件系统默认设置仅会转移问题,而非解决问题。 - 当程序尝试写入
/shr/def目录并需使其归属aaa:sharedef时,会遇到相同问题。
补充信息:
关于权限设置,C# API已在数年前更新,可通过以下代码创建权限为775的目录:
Directory.CreateDirectory("/shr/abc/myfolder1", UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute | UnixFileMode.OtherRead | UnixFileMode.OtherExecute);
解决方案
要让创建的目录归属为aaa:shareabc,需要在创建目录后手动修改其组所有权。以下是具体实现方式:
.NET 7+ 原生实现(推荐)
.NET 7及以上版本提供了UnixGroupInfo类,可以直接获取组ID,无需P/Invoke:
using System.IO; using System.Security.Principal; var targetDir = "/shr/abc/myfolder1"; var targetGroup = "shareabc"; // 创建目录并设置775权限 Directory.CreateDirectory(targetDir, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute | UnixFileMode.OtherRead | UnixFileMode.OtherExecute); // 获取目标组的ID var groupInfo = new UnixGroupInfo(targetGroup); int groupId = groupInfo.GroupId; // 修改目录的组所有者 var dirInfo = new DirectoryInfo(targetDir); dirInfo.UnixFileInfo.GroupOwnerId = groupId;
兼容旧版本.NET(.NET Core 3.1/.NET 5/.NET 6)
如果使用的是较低版本的.NET,可以通过P/Invoke调用系统libc的getgrnam函数获取组ID,再修改目录组所有权:
using System; using System.IO; using System.Runtime.InteropServices; var targetDir = "/shr/abc/myfolder1"; var targetGroup = "shareabc"; // 创建目录并设置775权限 Directory.CreateDirectory(targetDir, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute | UnixFileMode.OtherRead | UnixFileMode.OtherExecute); // 获取目标组ID int groupId = GetGroupId(targetGroup); // 修改目录的组所有者 var dirInfo = new DirectoryInfo(targetDir); dirInfo.UnixFileInfo.GroupOwnerId = groupId; // P/Invoke 获取组ID的实现 [DllImport("libc", SetLastError = true)] private static extern IntPtr getgrnam(string name); private static int GetGroupId(string groupName) { IntPtr grpPtr = getgrnam(groupName); if (grpPtr == IntPtr.Zero) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } Group grp = Marshal.PtrToStructure<Group>(grpPtr); return grp.gr_gid; } [StructLayout(LayoutKind.Sequential)] private struct Group { public IntPtr gr_name; public IntPtr gr_passwd; public int gr_gid; public IntPtr gr_mem; }
说明:
- 以上代码仅在Unix-like系统(Linux、macOS等)下有效,Windows系统不支持
UnixFileInfo及相关API。 - 运行程序的用户
aaa需要具备修改目标目录组所有权的权限,由于aaa属于shareabc组,且父目录/shr/abc的组是shareabc,因此权限是足够的。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

