You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

463 lines
21 KiB

10 years ago
using Alphaleonis.Win32.Filesystem;
using System;
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant <ai@example.com>
4 weeks ago
using System.Collections.Generic;
10 years ago
using System.Security.AccessControl;
namespace Security2
{
public class FileSystemSecurity2
{
protected FileSystemInfo item;
protected FileSystemSecurity sd;
protected AccessControlSections sections;
protected bool isFile = false;
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant <ai@example.com>
4 weeks ago
// The SDDL form of each section as it was read or last written, so that WriteChanges writes only the
// sections that changed since.
private Dictionary<AccessControlSections, string> sectionsAsRead;
10 years ago
public FileSystemInfo Item
{
get { return item; }
set { item = value; }
}
// An object that wraps a descriptor directly, such as the conversion from FileSecurity or DirectorySecurity,
// has no item and so no path to report; null reads more naturally here than an empty string, which would look
// like the path of an item whose name happens to be empty.
public string FullName { get { return item != null ? item.FullName : null; } }
10 years ago
public string Name { get { return item != null ? item.Name : null; } }
10 years ago
public bool IsFile { get { return isFile; } }
public FileSystemSecurity2(FileSystemInfo item, AccessControlSections sections)
{
this.sections = sections;
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
this.item = item;
isFile = item is FileInfo;
10 years ago
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
sd = GetSecurity(item, sections);
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
RememberSections();
10 years ago
}
// Wraps a descriptor that a caller created directly, such as the conversions from FileSecurity and
fix(security2): keep a descriptor without an item usable by the cmdlets that take a security descriptor PowerShell&#39;s parameter binder uses the conversion that 3.1 fixed for every -SecurityDescriptor parameter, so a descriptor that Get-Acl returns, never meant for these cmdlets, now binds where it used to be a binding error. Two more consequences of that surfaced: With the default module setting GetInheritedFrom ($true), Get-NTFSAccess, Get-NTFSAudit, Get-NTFSOrphanedAccess, and Get-NTFSOrphanedAudit stopped with the InvalidOperationException that a previous fix added for a descriptor without an item; a cmdlet user has no parameter to avoid it. GetFileSystemAccessRules and GetFileSystemAuditRules now treat getInheritedFrom as false for such a descriptor instead of throwing: the entries come back with no source, the same as an entry whose source Windows cannot name. The private constructor that wraps a descriptor directly marked every section, including Audit, as read; Get-Acl never reads Audit unless -Audit is given. This made HasAuditSection wrongly report true, so the existing #109 check let Get-NTFSAudit go on with no audit data instead of reporting that the descriptor has none, and let Get-NTFSInheritance report AuditInheritanceEnabled as a real value instead of null (unknown). The constructor now includes Audit in the sections only when the descriptor&#39;s binary form actually carries a SACL, the same test HasSystemAcl already uses. Get-NTFSOwner, Get-NTFSSimpleAccess, Get-NTFSOrphanedAccess, Get-NTFSEffectiveAccess, and Add-/Remove-NTFSAccess with such a descriptor already worked; Set-NTFSSecurityDescriptor already reported a clear, non-terminating error. A review finding on the earlier fix is also addressed: two assertions read a property through reflection instead of a plain PowerShell property access, which silently turns a throwing getter into $null without a trace and so could not tell a getter that throws from one that legitimately returns null. Every existing behavior for an item that is not null is unchanged. Public signatures are unchanged. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
1 week ago
// DirectorySecurity do; the object has no item. Access, Owner, and Group are always part of such a
// descriptor; Audit is part of it only when the descriptor actually carries a SACL (the same test as
// HasSystemAcl), since there was no read to tell sections apart otherwise. Without this, a descriptor that
// Get-Acl returned without -Audit reported HasAuditSection as true, so TestAuditSection (#109) let the audit
// cmdlets go on with no audit data at all, instead of reporting that the descriptor has none.
private FileSystemSecurity2(FileSystemSecurity sd)
{
this.sd = sd;
isFile = sd is FileSecurity;
fix(security2): keep a descriptor without an item usable by the cmdlets that take a security descriptor PowerShell&#39;s parameter binder uses the conversion that 3.1 fixed for every -SecurityDescriptor parameter, so a descriptor that Get-Acl returns, never meant for these cmdlets, now binds where it used to be a binding error. Two more consequences of that surfaced: With the default module setting GetInheritedFrom ($true), Get-NTFSAccess, Get-NTFSAudit, Get-NTFSOrphanedAccess, and Get-NTFSOrphanedAudit stopped with the InvalidOperationException that a previous fix added for a descriptor without an item; a cmdlet user has no parameter to avoid it. GetFileSystemAccessRules and GetFileSystemAuditRules now treat getInheritedFrom as false for such a descriptor instead of throwing: the entries come back with no source, the same as an entry whose source Windows cannot name. The private constructor that wraps a descriptor directly marked every section, including Audit, as read; Get-Acl never reads Audit unless -Audit is given. This made HasAuditSection wrongly report true, so the existing #109 check let Get-NTFSAudit go on with no audit data instead of reporting that the descriptor has none, and let Get-NTFSInheritance report AuditInheritanceEnabled as a real value instead of null (unknown). The constructor now includes Audit in the sections only when the descriptor&#39;s binary form actually carries a SACL, the same test HasSystemAcl already uses. Get-NTFSOwner, Get-NTFSSimpleAccess, Get-NTFSOrphanedAccess, Get-NTFSEffectiveAccess, and Add-/Remove-NTFSAccess with such a descriptor already worked; Set-NTFSSecurityDescriptor already reported a clear, non-terminating error. A review finding on the earlier fix is also addressed: two assertions read a property through reflection instead of a plain PowerShell property access, which silently turns a throwing getter into $null without a trace and so could not tell a getter that throws from one that legitimately returns null. Every existing behavior for an item that is not null is unchanged. Public signatures are unchanged. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
1 week ago
sections = AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group;
if (new RawSecurityDescriptor(sd.GetSecurityDescriptorBinaryForm(), 0).SystemAcl != null)
{
sections |= AccessControlSections.Audit;
}
RememberSections();
}
10 years ago
public FileSystemSecurity2(FileSystemInfo item)
{
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
this.item = item;
isFile = item is FileInfo;
10 years ago
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
try
{
sd = GetSecurity(item, AccessControlSections.All);
sections = AccessControlSections.All;
10 years ago
}
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
catch
10 years ago
{
try
{
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
sd = GetSecurity(item, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group);
sections = AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group;
10 years ago
}
catch
{
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
sd = GetSecurity(item, AccessControlSections.Access);
sections = AccessControlSections.Access;
10 years ago
}
}
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
// Read together with the SACL, the inherited entries of a DACL without the auto-inherit flag lose their
// inherited flag when the parent folder has no SACL, and writing such a DACL back stores them as explicit
// entries. Read alone, the DACL keeps the flags.
fix(security): report one audit inheritance by path and by descriptor The module&#39;s own tests ran on Windows Server 2019, 2022, and 2025 and on Windows 11 in the operating-system matrix. Two tests failed on every domain-joined machine and passed on the development host: - Get-NTFSInheritance -SecurityDescriptor reported AuditInheritanceEnabled as $false for an item without audit entries, where -Path reported $true. On these computers Windows reports the SACL as protected from inheritance when it reads all sections together, and as not protected when it reads the SACL alone. The descriptor now takes the audit section from a separate read, like it already did for the access section. Write() stores the sections that were read, so a descriptor with the wrong flag would also have written the SACL back as protected. - Get-NTFSEffectiveAccess -ServerName &#39;&#39; wrote an &#34;Access is denied&#34; error on computers where Windows takes an empty name for this computer. An empty name no longer asks the remote interface of the authorization manager; the cmdlet warns and returns the result of this computer, like for any name that can&#39;t be reached. The existing tests that found them stay as the regression guards: they were red on three virtual machines (Windows Server 2022 and 2025, Windows 11) and are green after the change. Three tests are new: the audit state of a file and a folder without audit entries, and of a file and a folder with disabled audit inheritance, by path and by descriptor. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
3 weeks ago
//
// The same goes for the SACL: read together with the other sections, the SACL of an item without audit
// entries is reported as protected from inheritance on some computers (seen on domain-joined Windows Server
// 2022 and 2025 and on Windows 11), while the read of the SACL alone, which Get-NTFSInheritance uses for a
// path, reports it as not protected. The state of the item has to be the same by path and by descriptor.
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
if (HasAuditSection)
{
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
var accessSecurity = GetSecurity(item, AccessControlSections.Access);
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
sd.SetSecurityDescriptorBinaryForm(accessSecurity.GetSecurityDescriptorBinaryForm(), AccessControlSections.Access);
fix(security): report one audit inheritance by path and by descriptor The module&#39;s own tests ran on Windows Server 2019, 2022, and 2025 and on Windows 11 in the operating-system matrix. Two tests failed on every domain-joined machine and passed on the development host: - Get-NTFSInheritance -SecurityDescriptor reported AuditInheritanceEnabled as $false for an item without audit entries, where -Path reported $true. On these computers Windows reports the SACL as protected from inheritance when it reads all sections together, and as not protected when it reads the SACL alone. The descriptor now takes the audit section from a separate read, like it already did for the access section. Write() stores the sections that were read, so a descriptor with the wrong flag would also have written the SACL back as protected. - Get-NTFSEffectiveAccess -ServerName &#39;&#39; wrote an &#34;Access is denied&#34; error on computers where Windows takes an empty name for this computer. An empty name no longer asks the remote interface of the authorization manager; the cmdlet warns and returns the result of this computer, like for any name that can&#39;t be reached. The existing tests that found them stay as the regression guards: they were red on three virtual machines (Windows Server 2022 and 2025, Windows 11) and are green after the change. Three tests are new: the audit state of a file and a folder without audit entries, and of a file and a folder with disabled audit inheritance, by path and by descriptor. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
3 weeks ago
var auditSecurity = GetSecurity(item, AccessControlSections.Audit);
sd.SetSecurityDescriptorBinaryForm(auditSecurity.GetSecurityDescriptorBinaryForm(), AccessControlSections.Audit);
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
}
RememberSections();
10 years ago
}
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
// For the root of a drive, the methods of DirectoryInfo read and write the security descriptor of the volume,
// a device object, instead of that of its root folder (#41). The methods that take the path keep the trailing
// backslash and reach the root folder.
internal static FileSystemSecurity GetSecurity(FileSystemInfo item, AccessControlSections sections)
{
var file = item as FileInfo;
if (file != null)
{
return file.GetAccessControl(sections);
}
string root;
if (TryGetDriveRoot(item, out root))
{
return Directory.GetAccessControl(root, sections);
}
return ((DirectoryInfo)item).GetAccessControl(sections);
}
internal static void SetSecurity(FileSystemInfo item, FileSystemSecurity security, AccessControlSections sections)
{
var file = item as FileInfo;
if (file != null)
{
file.SetAccessControl((FileSecurity)security, sections);
return;
}
string root;
if (TryGetDriveRoot(item, out root))
{
Directory.SetAccessControl(root, (DirectorySecurity)security, sections);
return;
}
((DirectoryInfo)item).SetAccessControl((DirectorySecurity)security, sections);
}
private static bool TryGetDriveRoot(FileSystemInfo item, out string root)
{
var fullName = item.FullName.TrimEnd('\\');
// A drive letter, such as C:
var isDriveLetter = fullName.Length == 2 && fullName[1] == ':' &&
((fullName[0] >= 'A' && fullName[0] <= 'Z') || (fullName[0] >= 'a' && fullName[0] <= 'z'));
// A volume name, such as \\?\Volume{9f122b1b-858a-49bd-aec4-dfbe8978fe16}, without a folder below it
var isVolumeName = fullName.StartsWith(@"\\?\Volume{", StringComparison.OrdinalIgnoreCase) &&
fullName.EndsWith("}", StringComparison.Ordinal) && fullName.IndexOf('\\', 4) < 0;
if (isDriveLetter || isVolumeName)
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
{
root = fullName + "\\";
return true;
}
root = null;
return false;
}
// Without the Security privilege, the security descriptor is read without its SACL.
internal bool HasAuditSection
{
get { return (sections & AccessControlSections.Audit) == AccessControlSections.Audit; }
}
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
// An item without audit entries can have no SACL at all, also when the SACL was read.
internal bool HasSystemAcl
{
get { return new RawSecurityDescriptor(sd.GetSecurityDescriptorBinaryForm(), 0).SystemAcl != null; }
}
// The sections that differ from the ones that were read or last written.
internal AccessControlSections ChangedSections
{
get
{
var changed = AccessControlSections.None;
foreach (var section in sectionsAsRead)
{
if (sd.GetSecurityDescriptorSddlForm(section.Key) != section.Value)
{
changed |= section.Key;
}
}
return changed;
}
}
private void RememberSections()
{
sectionsAsRead = new Dictionary<AccessControlSections, string>();
foreach (var section in new[] { AccessControlSections.Access, AccessControlSections.Audit, AccessControlSections.Owner, AccessControlSections.Group })
{
sectionsAsRead[section] = sd.GetSecurityDescriptorSddlForm(section);
}
}
10 years ago
public FileSystemSecurity SecurityDescriptor
{
get
{
return sd;
}
}
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
// Writes the sections that the descriptor was read with. Windows can return the owner and the group with a DACL
// that is read alone, and writing them back fails for an owner that the user cannot assign (#34).
10 years ago
public void Write()
{
RequireItem();
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
SetSecurity(item, sd, sections);
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
RememberSections();
}
// Writes only the sections that changed since they were read or last written, so that, for example, an
// unchanged owner that the user cannot assign isn't written back (#34). Without a change, it writes nothing.
internal void WriteChanges()
{
RequireItem();
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
var changedSections = ChangedSections;
if (changedSections == AccessControlSections.None)
{
return;
}
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
SetSecurity(item, sd, changedSections);
fix: write only the sections of the security descriptor that a cmdlet changes The access and audit cmdlets wrote the owner of an item back with the entries they changed. For a DACL without the auto-inherit flag, Windows returns the owner and the group even when only the DACL is read, and the cmdlets wrote every section that the descriptor held. Without the Restore privilege, or on a file server that refuses the owner, the write failed with error 1307 (#34). - Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit read only the DACL or the SACL. FileSystemSecurity2.Write(), Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they read, which also fixes the access inheritance cmdlets. - Read together with the SACL, the inherited entries of such a DACL lose their inherited flag when the parent folder has no SACL, and the cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now reads the DACL in a separate call. - Set-NTFSSecurityDescriptor writes only the sections that changed since they were read; an unchanged descriptor writes nothing (maintainer decision of 2026-10-06). - Clear-NTFSAudit writes nothing for an item without a SACL, and reports an error without the Security privilege (maintainer decision). The regression tests failed before and pass after the fix in Windows PowerShell 5.1 and PowerShell 7, elevated and as a basic user. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
RememberSections();
10 years ago
}
// Write() and WriteChanges() write to the item that the descriptor was read from; an object that a conversion
// from FileSecurity or DirectorySecurity created has none. Without this check they would fail with a
// NullReferenceException deep in SetSecurity instead of saying what is missing.
private void RequireItem()
{
if (item == null)
{
throw new InvalidOperationException(
"This security descriptor has no item to write to: it was created directly from a FileSecurity or " +
"DirectorySecurity object, not read from a file or folder. Set the Item property first, or call " +
"Write(FileSystemInfo) or Write(string) to choose where to write it.");
}
}
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
// Writes the sections that the descriptor was read with to another item, like Write().
10 years ago
public void Write(FileSystemInfo item)
{
fix: drive roots, audit descriptors, -WhatIf conflicts, and small items - #41: for the root of a drive, the cmdlets read and changed the security descriptor of the drive, a device object. FileSystemSecurity2 now routes drive roots through the path-based AlphaFS methods, which keep the trailing backslash; the removal and inheritance helpers use it too. - #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor without the audit entries like Get-NTFSAudit, through one helper, and Get-NTFSEffectiveAccess names the cause that Windows reported instead of a missing Security privilege. - #108: Copy-Item2 and Move-Item2 check the destination only for an operation that runs; with -WhatIf, a verbose message names the conflict. - #111: Disable-Privileges skips the privileges that the token doesn&#39;t hold, the privilege messages are spelled right, and Get-FileHash2 declares the type name of its objects; 05-Releasing.md documents the release metadata tests. - rc3 review leftovers: Remove-NTFSAudit writes nothing for an item without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores the previous owner in a finally block and keeps an owner that the descriptor sets, and FileSystemSecurity2.Write with another item writes only the sections that were read. Each fix has a test that failed first, in Windows PowerShell 5.1 and PowerShell 7; writing a drive root was checked once on a temporary VHD. Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt; Co-authored-by: AI Assistant &lt;ai@example.com&gt;
4 weeks ago
SetSecurity(item, sd, sections);
10 years ago
}
public void Write(string path)
{
FileSystemInfo item = null;
if (File.Exists(path))
{
item = new FileInfo(path);
}
else if (Directory.Exists(path))
{
item = new DirectoryInfo(path);
}
else
{
throw new System.IO.FileNotFoundException("File not found", path);
}
Write(item);
}
#region Conversion
// The descriptor is in sd. Before 5.0.0-rc6, these conversions returned fields that were never set, so they
// gave null, and the hash code threw a NullReferenceException. Before 5.0.0, the conversions the other way
// (from FileSecurity/DirectorySecurity) ignored the descriptor that was passed and returned an object built
// from an empty path, which always threw; they now keep the descriptor that was passed, see the private
// constructor above.
10 years ago
public static implicit operator FileSecurity(FileSystemSecurity2 fs2)
{
return fs2.sd as FileSecurity;
10 years ago
}
public static implicit operator FileSystemSecurity2(FileSecurity fs)
{
return new FileSystemSecurity2((FileSystemSecurity)fs);
10 years ago
}
public static implicit operator DirectorySecurity(FileSystemSecurity2 fs2)
{
return fs2.sd as DirectorySecurity;
10 years ago
}
public static implicit operator FileSystemSecurity2(DirectorySecurity fs)
{
return new FileSystemSecurity2((FileSystemSecurity)fs);
10 years ago
}
// Like the descriptors of .NET, two objects are equal when they wrap the same descriptor; a descriptor of .NET
// is never equal, so that equality is the same in both directions. A cast instead of "as" threw an
// InvalidCastException.
10 years ago
public override bool Equals(object obj)
{
var other = obj as FileSystemSecurity2;
return other != null && ReferenceEquals(sd, other.sd);
10 years ago
}
public override int GetHashCode()
{
return sd != null ? sd.GetHashCode() : 0;
10 years ago
}
#endregion
public static void ConvertToFileSystemFlags(ApplyTo ApplyTo, out InheritanceFlags inheritanceFlags, out PropagationFlags propagationFlags)
{
inheritanceFlags = InheritanceFlags.None;
propagationFlags = PropagationFlags.None;
switch (ApplyTo)
{
case ApplyTo.FilesOnly:
inheritanceFlags = InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.InheritOnly;
break;
case ApplyTo.SubfoldersAndFilesOnly:
inheritanceFlags = InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit;
propagationFlags = PropagationFlags.InheritOnly;
break;
case ApplyTo.SubfoldersOnly:
inheritanceFlags = InheritanceFlags.ContainerInherit;
propagationFlags = PropagationFlags.InheritOnly;
break;
case ApplyTo.ThisFolderAndFiles:
inheritanceFlags = InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.None;
break;
case ApplyTo.ThisFolderAndSubfolders:
inheritanceFlags = InheritanceFlags.ContainerInherit;
propagationFlags = PropagationFlags.None;
break;
case ApplyTo.ThisFolderOnly:
inheritanceFlags = InheritanceFlags.None;
propagationFlags = PropagationFlags.None;
break;
case ApplyTo.ThisFolderSubfoldersAndFiles:
inheritanceFlags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.None;
break;
case ApplyTo.FilesOnlyOneLevel:
inheritanceFlags = InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit;
break;
case ApplyTo.SubfoldersAndFilesOnlyOneLevel:
inheritanceFlags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit;
break;
case ApplyTo.SubfoldersOnlyOneLevel:
inheritanceFlags = InheritanceFlags.ContainerInherit;
propagationFlags = PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit;
break;
case ApplyTo.ThisFolderAndFilesOneLevel:
inheritanceFlags = InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.NoPropagateInherit;
break;
case ApplyTo.ThisFolderAndSubfoldersOneLevel:
inheritanceFlags = InheritanceFlags.ContainerInherit;
propagationFlags = PropagationFlags.NoPropagateInherit;
break;
case ApplyTo.ThisFolderSubfoldersAndFilesOneLevel:
inheritanceFlags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
propagationFlags = PropagationFlags.NoPropagateInherit;
break;
}
}
public static ApplyTo ConvertToApplyTo(InheritanceFlags InheritanceFlags, PropagationFlags PropagationFlags)
{
if (InheritanceFlags == InheritanceFlags.ObjectInherit & PropagationFlags == PropagationFlags.InheritOnly)
return ApplyTo.FilesOnly;
else if (InheritanceFlags == (InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit) & PropagationFlags == PropagationFlags.InheritOnly)
return ApplyTo.SubfoldersAndFilesOnly;
else if (InheritanceFlags == InheritanceFlags.ContainerInherit & PropagationFlags == PropagationFlags.InheritOnly)
return ApplyTo.SubfoldersOnly;
else if (InheritanceFlags == InheritanceFlags.ObjectInherit & PropagationFlags == PropagationFlags.None)
return ApplyTo.ThisFolderAndFiles;
else if (InheritanceFlags == InheritanceFlags.ContainerInherit & PropagationFlags == PropagationFlags.None)
return ApplyTo.ThisFolderAndSubfolders;
else if (InheritanceFlags == InheritanceFlags.None & PropagationFlags == PropagationFlags.None)
return ApplyTo.ThisFolderOnly;
else if (InheritanceFlags == (InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit) & PropagationFlags == PropagationFlags.None)
return ApplyTo.ThisFolderSubfoldersAndFiles;
else if (InheritanceFlags == (InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit) & PropagationFlags == PropagationFlags.NoPropagateInherit)
return ApplyTo.ThisFolderSubfoldersAndFilesOneLevel;
else if (InheritanceFlags == InheritanceFlags.ContainerInherit & PropagationFlags == PropagationFlags.NoPropagateInherit)
return ApplyTo.ThisFolderAndSubfoldersOneLevel;
else if (InheritanceFlags == InheritanceFlags.ObjectInherit & PropagationFlags == PropagationFlags.NoPropagateInherit)
return ApplyTo.ThisFolderAndFilesOneLevel;
else if (InheritanceFlags == (InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit) & PropagationFlags == (PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit))
return ApplyTo.SubfoldersAndFilesOnlyOneLevel;
else if (InheritanceFlags == InheritanceFlags.ContainerInherit & PropagationFlags == (PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit))
return ApplyTo.SubfoldersOnlyOneLevel;
else if (InheritanceFlags == InheritanceFlags.ObjectInherit & PropagationFlags == (PropagationFlags.InheritOnly | PropagationFlags.NoPropagateInherit))
return ApplyTo.FilesOnlyOneLevel;
throw new RightsConverionException("The combination of InheritanceFlags and PropagationFlags could not be translated");
}
public static FileSystemRights MapGenericRightsToFileSystemRights(uint originalRights)
{
try
{
var r = Enum.Parse(typeof(FileSystemRights), (originalRights).ToString());
if (r.ToString() == originalRights.ToString())
{
throw new ArgumentOutOfRangeException();
}
var fileSystemRights = (FileSystemRights)originalRights;
return fileSystemRights;
}
catch (Exception)
{
FileSystemRights rights = 0;
if (Convert.ToBoolean(originalRights & (uint)GenericRights.GENERIC_EXECUTE))
{
rights |= (FileSystemRights)MappedGenericRights.FILE_GENERIC_EXECUTE;
originalRights ^= (uint)GenericRights.GENERIC_EXECUTE;
}
if (Convert.ToBoolean(originalRights & (uint)GenericRights.GENERIC_READ))
{
rights |= (FileSystemRights)MappedGenericRights.FILE_GENERIC_READ;
originalRights ^= (uint)GenericRights.GENERIC_READ;
}
if (Convert.ToBoolean(originalRights & (uint)GenericRights.GENERIC_WRITE))
{
rights |= (FileSystemRights)MappedGenericRights.FILE_GENERIC_WRITE;
originalRights ^= (uint)GenericRights.GENERIC_WRITE;
}
if (Convert.ToBoolean(originalRights & (uint)GenericRights.GENERIC_ALL))
{
rights |= (FileSystemRights)MappedGenericRights.FILE_GENERIC_ALL;
originalRights ^= (uint)GenericRights.GENERIC_ALL;
}
//throw new RightsConverionException("Cannot convert GenericRights into FileSystemRights");
var remainingRights = (FileSystemRights)Enum.Parse(typeof(FileSystemRights), (originalRights).ToString());
rights |= remainingRights;
return rights;
}
}
}
}