C#使用DirectorySecurity的SetOwner设置文件夹所有者只能设为当前用户
看到有人问了这个问题,查询并实验后发现确实有这个问题,测试代码如下:
static void Main(string[] args)
{
try
{
string folderPath = @"e:\testSecurity";
Directory.CreateDirectory(folderPath);
DirectorySecurity defaultFSec = Directory.GetAccessControl(folderPath);
IdentityReference newUser = new NTAccount("User2");
defaultFSec.SetOwner(newUser);
Directory.SetAccessControl(folderPath, defaultFSec);
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadLine();
}结果是System.InvalidOperationException: The security identifier is not allowed to be the owner of this object.
2005年在Microsoft Connect上就有人提出了这个问题,微软说下个版本将会考虑添加这个功能,然而现在似乎还不行。
当然总是有什么其他办法的,既然资源管理器可以做到,那么直接调用API就是一个可行的办法。Richard Willis给出了一个实现方法,我这里把他的代码转贴一下:
sealed class UnmanagedCode
{
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
// Use this signature if you do not want the previous state
[DllImport("advapi32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AdjustTokenPrivileges(IntPtr tokenHandle,
[MarshalAs(UnmanagedType.Bool)]bool disableAllPrivileges,
ref TOKEN_PRIVILEGES newState,
UInt32 bufferLength,
IntPtr previousState,
IntPtr returnLength);
[DllImport("kernel32.dll", ExactSpelling = true)]
static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
static extern bool OpenProcessToken
(IntPtr processHandle, int desiredAccess, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LookupPrivilegeValue
(string host, string name, ref LUID lpLuid);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TOKEN_PRIVILEGES
{
public UInt32 PrivilegeCount;
public LUID Luid;
public UInt32 Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public uint LowPart;
public int HighPart;
}
const int SE_PRIVILEGE_ENABLED = 0x00000002;
const int TOKEN_QUERY = 0x00000008;
const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
//http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx
const string SE_RESTORE_PRIVILEGE = "SeRestorePrivilege";
public static void GiveRestorePrivilege()
{
TOKEN_PRIVILEGES tokenPrivileges;
tokenPrivileges.PrivilegeCount = 1;
tokenPrivileges.Luid = new LUID();
tokenPrivileges.Attributes = SE_PRIVILEGE_ENABLED;
IntPtr tokenHandle = RetrieveProcessToken();
try
{
bool success = LookupPrivilegeValue
(null, SE_RESTORE_PRIVILEGE, ref tokenPrivileges.Luid);
if (success == false)
{
int lastError = Marshal.GetLastWin32Error();
throw new Exception(
string.Format("Could not find privilege {0}. Error {1}",
SE_RESTORE_PRIVILEGE, lastError));
}
success = AdjustTokenPrivileges(
tokenHandle, false,
ref tokenPrivileges, 0,
IntPtr.Zero, IntPtr.Zero);
if (success == false)
{
int lastError = Marshal.GetLastWin32Error();
throw new Exception(
string.Format("Could not assign privilege {0}. Error {1}",
SE_RESTORE_PRIVILEGE, lastError));
}
}
finally
{
CloseHandle(tokenHandle);
}
}
static IntPtr RetrieveProcessToken()
{
IntPtr processHandle = GetCurrentProcess();
IntPtr tokenHandle = IntPtr.Zero;
bool success = OpenProcessToken(processHandle,
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
ref tokenHandle);
if (success == false)
{
int lastError = Marshal.GetLastWin32Error();
throw new Exception(
string.Format("Could not retrieve process token. Error {0}",
lastError));
}
return tokenHandle;
}
}


