Использование System.Security.AccessControl для удаления ACE из папки ACL очень медленный, если размер целевой папки очень большой

Мне нужно удалить определенные учетные записи (например, опекуна "Все" или какую-либо глобальную группу, которая предоставляет всем доступ к папке) из ACL-списка в определенной подпапке общего ресурса. Я получаю объект DirectorySecurity, получаю и перебираю AuthorizationRuleCollection, удаляю рассматриваемый AccessRule из ACL, а затем вызываю SetAccessControl для применения изменений. Все работает нормально, если целевая папка небольшая, но если в ней много дочерних папок и файлов, для внесения изменений может потребоваться вечность (гораздо дольше, чем просто сделать это вручную). Я только хочу иметь дело с ACL на целевой папке. Есть ли способ сделать это с помощью классов.net DirectorySecurity? Или мне нужно прибегнуть к Win32 API или какому-то другому решению? Благодарю.

Вот фрагмент кода. Вызов dirInfo.SetAccessControl(dirSec) находится там, где он зависает, когда размер папки очень большой.

DirectoryInfo dirInfo = new DirectoryInfo(path);
DirectorySecurity dirSec = dirInfo.GetAccessControl();
AuthorizationRuleCollection acl = dirSec.GetAccessRules(true, true,     
                                  typeof(System.Security.Principal.NTAccount));
foreach (FileSystemAccessRule ace in acl)
{
    if (groupsToRemove.Contains(ace.IdentityReference.Value))
    {
        dirSec.RemoveAccessRuleSpecific(ace);
        dirInfo.SetAccessControl(dirSec);
    }
}

2 ответа

Решение
DirectoryInfo dirInfo = new DirectoryInfo(path);
DirectorySecurity dirSec = dirInfo.GetAccessControl();
AuthorizationRuleCollection acl = dirSec.GetAccessRules(true, true,     
                                  typeof(System.Security.Principal.NTAccount));
foreach (FileSystemAccessRule ace in acl)
{
    if (groupsToRemove.Contains(ace.IdentityReference.Value))
    {
        dirSec.RemoveAccessRuleSpecific(ace);
        dirInfo.SetAccessControl(dirSec);
    }
}

В вашем коде вы применяете обновления к ACL при каждом взаимодействии вашего цикла, это становится очень дорогим. Вы пробовали двигаться dirInfo.SetAccessControl(dirSec); вне foreach? Это должно ссылаться на SetAccessControl метод на вашем DirectoryInfo объект один раз, применяя все изменения за один проход, например так:

foreach (FileSystemAccessRule ace in acl)
{
    if (groupsToRemove.Contains(ace.IdentityReference.Value))
    {
        dirSec.RemoveAccessRuleSpecific(ace);        
    }
}
dirInfo.SetAccessControl(dirSec);

Необходимо установить флаг SE_DACL_PROTECTED, чтобы "предотвратить применение ACE, заданных в DACL родительского контейнера, и любых объектов над родительским контейнером в иерархии каталогов, к объекту DACL". Это, скорее всего, ускорит вашу работу, потому что не придется применять все дочерние объекты. Также из MSDN,

Помните, что для установки SE_DACL_PROTECTED должен присутствовать флаг SE_DACL_PRESENT, а для установки SE_SACL_PROTECTED должен присутствовать SE_SACL_PRESENT.

Затем вы должны использовать свойство IADsSecurityDescriptor.Control, чтобы контролировать, наследуются ли DACL и SACL объектом от его родительского контейнера.

Дополнительная информация доступна на MSDN для интерфейса IADsSecurityDescriptor.

Другие вопросы по тегам