Я попытался следовать предложенному OP шаблону для копирования ACL и атрибутов файла и обнаружил несколько проблем в нашем приложении. Надеюсь, эта информация поможет другим.
Согласно MSDN, использование метода File.SetAccessControl()
, как показано выше, не будет работать.
Метод SetAccessControl сохраняет только объекты FileSecurity, которые
были изменены после создания объекта. Если объект FileSecurity
не был изменен, он не будет сохранен в файл. Следовательно,
невозможно получить объект FileSecurity из одного файла и
повторно применить тот же объект к другому файлу .
Необходимо создать новый объект FileSecurity
для файла назначения и назначить ему копию исходного FileSecurity
объекта.
Вот пример шаблона, который работает из нашего приложения.
public static void CopyFile(string source, string destination)
{
// Copy the file
File.Copy(source, destination, true);
// Get the source file's ACLs
FileSecurity fileSecuritySource = File.GetAccessControl(source, AccessControlSections.All);
string sddlSource = fileSecuritySource.GetSecurityDescriptorSddlForm(AccessControlSections.All);
// Create the destination file's ACLs
FileSecurity fileSecurityDestination = new FileSecurity();
fileSecurityDestination.SetSecurityDescriptorSddlForm(sddlSource);
// Set the destination file's ACLs
File.SetAccessControl(destination, fileSecurityDestination);
// copy the file attributes now
FileAttributes fileAttributes = File.GetAttributes(source);
File.SetAttributes(destination, fileAttributes);
}
После исправления первой проблемы мы обнаружили, что атрибут владельца не копируется. Вместо этого целевой файл в нашем случае принадлежал Администратору. Чтобы обойти это, процесс должен включить привилегию SE_RESTORE_NAME. Проверьте AdjustTokenPrivileges
на pinvoke.net для полного класса привилегий, который облегчает настройку привилегий.
Последнее, с чем нам пришлось иметь дело, это UAC. Нам нужно было перезапустить наше приложение с правами администратора, если пользователь работает под управлением UAC. Вот еще один пример из нашего приложения, которое касается перезапуска приложения с повышенными привилегиями.
private static void RelaunchWithAdministratorRights(string[] args)
{
// Launch as administrator
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.UseShellExecute = true;
processStartInfo.WorkingDirectory = Environment.CurrentDirectory;
string executablePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
processStartInfo.FileName = executablePath;
processStartInfo.Verb = "runas";
if (args != null && args.Count() > 0)
{
string arguments = args[0];
for (int i = 1; i < args.Count(); i++)
arguments += " " + args[i];
processStartInfo.Arguments = arguments;
}
try
{
using (Process exeProcess = Process.Start(processStartInfo))
{
exeProcess.WaitForExit();
}
}
catch
{
// The user refused to allow privileges elevation. Do nothing and return directly ...
}
Environment.Exit(0);
}
Наше приложение было консольным, поэтому нам нужно передать параметр args
из метода Main
в RelaunchWithAdministratorRights(args)
. Неконсольские приложения могут вместо этого принимать значение NULL. Мы вызываем RelaunchWithAdministratorRights
из catch
блоков, как в:
try
{
...
}
catch (SecurityException)
{
RelaunchWithAdministratorRights(args);
return;
}
В нашем приложении мы просто возвращаемся после вызова RelaunchWithAdministratorRights
, чтобы выйти из экземпляра приложения, в котором не было привилегий. В зависимости от вашего приложения, вы можете выбрать throw
свой выход. В любом случае, после возврата из RelaunchWithAdministratorRights
вы не хотите, чтобы этот экземпляр продолжал обработку.
Наслаждайтесь!