From 27510301b84b0b8db2a31e5570384720b923e137 Mon Sep 17 00:00:00 2001 From: Kyle H Date: Fri, 30 Oct 2020 02:09:07 -0500 Subject: [PATCH 1/3] The -Stream parameter now works with directories --- .../namespaces/FileSystemProvider.cs | 109 ++++++++++++------ .../Add-Content.Tests.ps1 | 22 ++++ .../Clear-Content.Tests.ps1 | 64 ++++++---- .../Get-Content.Tests.ps1 | 38 +++--- .../Get-Item.Tests.ps1 | 26 ++++- .../Remove-Item.Tests.ps1 | 33 +++++- .../Set-Content.Tests.ps1 | 33 ++++++ 7 files changed, 247 insertions(+), 78 deletions(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index be4b95dc935..c6845f29dab 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1311,35 +1311,34 @@ protected override void GetItem(string path) // If we want to retrieve the file streams, retrieve them. if (retrieveStreams) { - if (!isContainer) + foreach (string desiredStream in dynamicParameters.Stream) { - foreach (string desiredStream in dynamicParameters.Stream) + // See that it matches the name specified + WildcardPattern p = WildcardPattern.Get(desiredStream, WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant); + bool foundStream = false; + + foreach (AlternateStreamData stream in AlternateDataStreamUtilities.GetStreams(result.FullName)) { - // See that it matches the name specified - WildcardPattern p = WildcardPattern.Get(desiredStream, WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant); - bool foundStream = false; + if (!p.IsMatch(stream.Stream)) + { continue; } - foreach (AlternateStreamData stream in AlternateDataStreamUtilities.GetStreams(result.FullName)) - { - if (!p.IsMatch(stream.Stream)) { continue; } + string outputPath = result.FullName + ":" + stream.Stream; + // Alternate data streams can never be containers. + WriteItemObject(stream, outputPath, false); + foundStream = true; + } - string outputPath = result.FullName + ":" + stream.Stream; - WriteItemObject(stream, outputPath, isContainer); - foundStream = true; - } + if ((!WildcardPattern.ContainsWildcardCharacters(desiredStream)) && (!foundStream)) + { + string errorMessage = StringUtil.Format( + FileSystemProviderStrings.AlternateDataStreamNotFound, desiredStream, result.FullName); + Exception e = new FileNotFoundException(errorMessage, result.FullName); - if ((!WildcardPattern.ContainsWildcardCharacters(desiredStream)) && (!foundStream)) - { - string errorMessage = StringUtil.Format( - FileSystemProviderStrings.AlternateDataStreamNotFound, desiredStream, result.FullName); - Exception e = new FileNotFoundException(errorMessage, result.FullName); - - WriteError(new ErrorRecord( - e, - "AlternateDataStreamNotFound", - ErrorCategory.ObjectNotFound, - path)); - } + WriteError(new ErrorRecord( + e, + "AlternateDataStreamNotFound", + ErrorCategory.ObjectNotFound, + path)); } } } @@ -6666,7 +6665,14 @@ public IContentReader GetContentReader(string path) try { - if (Directory.Exists(path)) + // Get-Content will write a non-terminating error if the target is a directory. + // On Windows, the streamName must be null or empty for it to write the error. Otherwise, the + // alternate data stream is not a directory, even if it's set on a directory. + if (Directory.Exists(path) +#if !UNIX + && string.IsNullOrEmpty(streamName) +#endif + ) { string errMsg = StringUtil.Format(SessionStateStrings.GetContainerContentException, path); ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "GetContainerContentException", ErrorCategory.InvalidOperation, null); @@ -6821,7 +6827,14 @@ public IContentWriter GetContentWriter(string path) try { - if (Directory.Exists(path)) + // Add-Content and Set-Content will write a non-terminating error if the target is a directory. + // On Windows, the streamName must be null or empty for it to write the error. Otherwise, the + // alternate data stream is not a directory, even if it's set on a directory. + if (Directory.Exists(path) +#if !UNIX + && string.IsNullOrEmpty(streamName) +#endif + ) { string errMsg = StringUtil.Format(SessionStateStrings.WriteContainerContentException, path); ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "WriteContainerContentException", ErrorCategory.InvalidOperation, null); @@ -6899,13 +6912,6 @@ public void ClearContent(string path) path = NormalizePath(path); - if (Directory.Exists(path)) - { - string errorMsg = StringUtil.Format(SessionStateStrings.ClearDirectoryContent, path); - WriteError(new ErrorRecord(new NotSupportedException(errorMsg), "ClearDirectoryContent", ErrorCategory.InvalidOperation, path)); - return; - } - try { #if !UNIX @@ -6957,6 +6963,26 @@ public void ClearContent(string path) clearStream = false; } +#endif + // On Windows, determine if our argument is a directory only after we determine if + // we're being asked to work with an alternate data stream, because directories can have + // alternate data streams on them that are not child items. These alternate data streams + // must be treated as data streams, even if they're attached to directories. However, + // if asked to work with a directory without a data stream specified, write a non-terminating + // error instead of clearing all child items of the directory. (On non-Windows, alternate + // data streams don't exist, so in that environment always write the error when addressing + // a directory.) + if (Directory.Exists(path) +#if !UNIX + && !clearStream +#endif + ) + { + string errorMsg = StringUtil.Format(SessionStateStrings.ClearDirectoryContent, path); + WriteError(new ErrorRecord(new NotSupportedException(errorMsg), "ClearDirectoryContent", ErrorCategory.InvalidOperation, path)); + return; + } +#if !UNIX if (clearStream) { FileStream fileStream = null; @@ -8622,7 +8648,21 @@ internal static List GetStreams(string path) SafeFindHandle handle = NativeMethods.FindFirstStreamW( path, NativeMethods.StreamInfoLevels.FindStreamInfoStandard, findStreamData, 0); - if (handle.IsInvalid) throw new Win32Exception(); + if (handle.IsInvalid) + { + int error = Marshal.GetLastWin32Error(); + + // Directories don't normally have alternate streams, so this is not an exceptional state. + // If a directory has no alternate data streams, FindFirstStreamW returns ERROR_HANDLE_EOF. + if (error == NativeMethods.ERROR_HANDLE_EOF) + { + return alternateStreams; + } + + // An unexpected error was returned, that we don't know how to interpret. The most helpful + // thing we can do at this point is simply throw the raw Win32 exception. + throw new Win32Exception(error); + } try { @@ -8757,6 +8797,7 @@ internal static void SetZoneOfOrigin(string path, SecurityZone securityZone) internal static class NativeMethods { internal const int ERROR_HANDLE_EOF = 38; + internal const int ERROR_INVALID_PARAMETER = 87; internal enum StreamInfoLevels { FindStreamInfoStandard = 0 } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Add-Content.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Add-Content.Tests.ps1 index d4bd23a6fee..e601971ba87 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Add-Content.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Add-Content.Tests.ps1 @@ -5,6 +5,7 @@ Describe "Add-Content cmdlet tests" -Tags "CI" { BeforeAll { $file1 = "file1.txt" Setup -File "$file1" + $streamContent = "ShouldWork" } Context "Add-Content should actually add content" { @@ -47,6 +48,27 @@ Describe "Add-Content cmdlet tests" -Tags "CI" { { Add-Content -Path . -Value "WriteContainerContentException" -ErrorAction Stop } | Should -Throw -ErrorId "WriteContainerContentException,Microsoft.PowerShell.Commands.AddContentCommand" } + Context "Add-Content should work with alternate data streams on Windows" { + BeforeAll { + if (!$isWindows) { + return + } + $ADSTestDir = "addcontentadstest" + $ADSTestFile = "addcontentads.txt" + $streamContent = "This is a test stream." + Setup -Directory "$ADSTestDir" + Setup -File "$ADSTestFile" + } + It "Should add an alternate data stream on a directory" -Skip:(!$IsWindows) { + Add-Content -Path TestDrive:\$ADSTestDir -Stream Add-Content-Test-Stream -Value $streamContent -ErrorAction Stop + Get-Content -Path TestDrive:\$ADSTestDir -Stream Add-Content-Test-Stream | Should -BeExactly $streamContent + } + It "Should add an alternate data stream on a file" -Skip:(!$IsWindows) { + Add-Content -Path TestDrive:\$ADSTestFile -Stream Add-Content-Test-Stream -Value $streamContent -ErrorAction Stop + Get-Content -Path TestDrive:\$ADSTestFile -Stream Add-Content-Test-Stream | Should -BeExactly $streamContent + } + } + #[BugId(BugDatabase.WindowsOutOfBandReleases, 906022)] It "should throw 'NotSupportedException' when you add-content to an unsupported provider" -Skip:($IsLinux -Or $IsMacOS) { { Add-Content -Path HKLM:\\software\\microsoft -Value "ShouldNotWorkBecausePathIsUnsupported" -ErrorAction Stop } | Should -Throw -ErrorId "NotSupported,Microsoft.PowerShell.Commands.AddContentCommand" diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Clear-Content.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Clear-Content.Tests.ps1 index e2ab56fc809..ff9d0c8f39c 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Clear-Content.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Clear-Content.Tests.ps1 @@ -51,6 +51,8 @@ Describe "Clear-Content cmdlet tests" -Tags "CI" { Setup -File "$file3" -Content $content2 $streamContent = "content for alternate stream" $streamName = "altStream1" + $dirName = "clearcontent" + Setup -Directory "$dirName" } Context "Clear-Content should actually clear content" { @@ -75,32 +77,50 @@ Describe "Clear-Content cmdlet tests" -Tags "CI" { $cci.SupportsShouldProcess | Should -BeTrue } - It "Alternate streams should be cleared with clear-content" -Skip:(!$IsWindows) { - # make sure that the content is correct - # this is here rather than BeforeAll because only windows can write to an alternate stream - Set-Content -Path "TestDrive:/$file3" -Stream $streamName -Value $streamContent - Get-Content -Path "TestDrive:/$file3" | Should -BeExactly $content2 - Get-Content -Path "TestDrive:/$file3" -Stream $streamName | Should -BeExactly $streamContent - Clear-Content -Path "TestDrive:/$file3" -Stream $streamName - Get-Content -Path "TestDrive:/$file3" | Should -BeExactly $content2 - Get-Content -Path "TestDrive:/$file3" -Stream $streamName | Should -BeNullOrEmpty - } + Context "Clear-Content should work with alternate data streams on Windows" { + It "Alternate streams should be cleared with Clear-Content on a file" -Skip:(!$IsWindows) { + + Set-Content -Path "TestDrive:/$file3" -Stream $streamName -Value $streamContent + Get-Content -Path "TestDrive:/$file3" -Stream $streamName | Should -BeExactly $streamContent + + Clear-Content -Path "TestDrive:/$file3" -Stream $streamName -ErrorAction Stop + + $result = Get-Item -Path "TestDrive:/$file3" -Stream $streamName + $result | Should -BeOfType System.Management.Automation.Internal.AlternateStreamData + $result.length | Should -Be 0 + } - It "the '-Stream' dynamic parameter is visible to get-command in the filesystem" -Skip:(!$IsWindows) { - try { - Push-Location -Path TestDrive: - (Get-Command Clear-Content -Stream foo).parameters.keys -eq "stream" | Should -Be "stream" + It "Alternate streams should be cleared with Clear-Content on a directory" -Skip:(!$IsWindows) { + Set-Content -Path "TestDrive:/$dirName" -Stream $streamName -Value $streamContent + + Get-Content -Path "TestDrive:/$dirName" -Stream $streamName | Should -BeExactly $streamContent + Clear-Content -Path "TestDrive:/$dirName" -Stream $streamName -ErrorAction Stop + + $result = Get-Item -Path "TestDrive:/$dirName" -Stream $streamName + $result | Should -BeOfType System.Management.Automation.Internal.AlternateStreamData + $result.length | Should -Be 0 } - finally { - Pop-Location + + It "the '-Stream' dynamic parameter is visible to get-command in the filesystem" -Skip:(!$IsWindows) { + try { + Push-Location -Path TestDrive: + (Get-Command Clear-Content -Stream foo).parameters.keys -eq "Stream" | Should -BeExactly "Stream" + } + finally { + Pop-Location + } } - } - It "the '-Stream' dynamic parameter should not be visible to get-command in the function provider" { - Push-Location -Path function: - { Get-Command Clear-Content -Stream $streamName } | - Should -Throw -ErrorId "NamedParameterNotFound,Microsoft.PowerShell.Commands.GetCommandCommand" - Pop-Location + It "the '-Stream' dynamic parameter should not be visible to get-command in the function provider" -Skip:(!$IsWindows) { + try { + Push-Location -Path function: + { Get-Command Clear-Content -Stream $streamName } | + Should -Throw -ErrorId "NamedParameterNotFound,Microsoft.PowerShell.Commands.GetCommandCommand" + } + finally { + Pop-Location + } + } } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Content.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Content.Tests.ps1 index 6c50cee5aa8..5fdc904580d 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Content.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Content.Tests.ps1 @@ -221,25 +221,27 @@ Describe "Get-Content" -Tags "CI" { $expected = 'He', 'o,', '', 'Wor', "d${nl}He", 'o2,', '', 'Wor', "d2${nl}" for ($i = 0; $i -lt $result.Length ; $i++) { $result[$i] | Should -BeExactly $expected[$i]} } + + Context "Alternate Data Stream support on Windows" { + It "Should support NTFS streams using colon syntax" -Skip:(!$IsWindows) { + Set-Content "${testPath}:Stream" -Value "Foo" + { Test-Path "${testPath}:Stream" | Should -Throw -ErrorId "ItemExistsNotSupportedError,Microsoft.PowerShell.Commands,TestPathCommand" } + Get-Content "${testPath}:Stream" | Should -BeExactly "Foo" + Get-Content $testPath | Should -BeExactly $testString + } - It "Should support NTFS streams using colon syntax" -Skip:(!$IsWindows) { - Set-Content "${testPath}:Stream" -Value "Foo" - { Test-Path "${testPath}:Stream" | Should -Throw -ErrorId "ItemExistsNotSupportedError,Microsoft.PowerShell.Commands,TestPathCommand" } - Get-Content "${testPath}:Stream" | Should -BeExactly "Foo" - Get-Content $testPath | Should -BeExactly $testString - } - - It "Should support NTFS streams using -Stream" -Skip:(!$IsWindows) { - Set-Content -Path $testPath -Stream hello -Value World - Get-Content -Path $testPath | Should -BeExactly $testString - Get-Content -Path $testPath -Stream hello | Should -BeExactly "World" - $item = Get-Item -Path $testPath -Stream hello - $item | Should -BeOfType System.Management.Automation.Internal.AlternateStreamData - $item.Stream | Should -BeExactly "hello" - Clear-Content -Path $testPath -Stream hello - Get-Content -Path $testPath -Stream hello | Should -BeNullOrEmpty - Remove-Item -Path $testPath -Stream hello - { Get-Content -Path $testPath -Stream hello | Should -Throw -ErrorId "GetContentReaderFileNotFoundError,Microsoft.PowerShell.Commands.GetContentCommand" } + It "Should support NTFS streams using -Stream" -Skip:(!$IsWindows) { + Set-Content -Path $testPath -Stream hello -Value World + Get-Content -Path $testPath | Should -BeExactly $testString + Get-Content -Path $testPath -Stream hello | Should -BeExactly "World" + $item = Get-Item -Path $testPath -Stream hello + $item | Should -BeOfType System.Management.Automation.Internal.AlternateStreamData + $item.Stream | Should -BeExactly "hello" + Clear-Content -Path $testPath -Stream hello + Get-Content -Path $testPath -Stream hello | Should -BeNullOrEmpty + Remove-Item -Path $testPath -Stream hello + { Get-Content -Path $testPath -Stream hello -ErrorAction stop} | Should -Throw -ErrorId "GetContentReaderFileNotFoundError,Microsoft.PowerShell.Commands.GetContentCommand" + } } It "Should support colons in filename on Linux/Mac" -Skip:($IsWindows) { diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Item.Tests.ps1 index 86b831ba8d0..c04412e91b4 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Item.Tests.ps1 @@ -112,16 +112,38 @@ Describe "Get-Item" -Tags "CI" { return } $altStreamPath = "$TESTDRIVE/altStream.txt" + $altStreamDirectory = "$TESTDRIVE/altstreamdir" + $noAltStreamDirectory = "$TESTDRIVE/noaltstreamdir" $stringData = "test data" $streamName = "test" - $item = New-Item -type file $altStreamPath + $absentStreamName = "noExist" + $null = New-Item -type file $altStreamPath Set-Content -Path $altStreamPath -Stream $streamName -Value $stringData + $null = New-Item -type directory $altStreamDirectory + Set-Content -Path $altStreamDirectory -Stream $streamName -Value $stringData + $null = New-Item -type directory $noAltStreamDirectory } - It "Should find an alternate stream if present" -Skip:$skipNotWindows { + It "Should find an alternate stream on a file if present" -Skip:$skipNotWindows { $result = Get-Item $altStreamPath -Stream $streamName $result.Length | Should -Be ($stringData.Length + [Environment]::NewLine.Length) $result.Stream | Should -Be $streamName } + It "Should error if it cannot find alternate stream on an existing file" -Skip:$skipNotWindows { + { Get-Item $altStreamPath -Stream $absentStreamName -ErrorAction Stop } | Should -Throw -ErrorId "AlternateDataStreamNotFound,Microsoft.PowerShell.Commands.GetItemCommand" + } + It "Should find an alternate stream on a directory if present, and it should not be a container" -Skip:$skipNotWindows { + $result = Get-Item $altStreamDirectory -Stream $streamName + $result.Length | Should -Be ($stringData.Length + [Environment]::NewLine.Length ) + $result.Stream | Should -Be $streamName + $result.PSIsContainer | Should -BeExactly $false + } + It "Should not find an alternate stream on a directory if not present" -Skip:$skipNotWindows { + { Get-Item $noAltStreamDirectory -Stream $absentStreamName -ErrorAction Stop } | Should -Throw -ErrorId "AlternateDataStreamNotFound,Microsoft.PowerShell.Commands.GetItemCommand" + } + It "Should find zero alt streams and not fail on a directory with a wildcard stream name if no alt streams are present" -Skip:$skipNotWindows { + $result = Get-Item $noAltStreamDirectory -Stream * -ErrorAction Stop + $result | Should -BeExactly $null + } } Context "Registry Provider" { diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Remove-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Remove-Item.Tests.ps1 index 769a8a34003..3e8ccb8fa91 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Remove-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Remove-Item.Tests.ps1 @@ -7,9 +7,7 @@ Describe "Remove-Item" -Tags "CI" { Context "File removal Tests" { BeforeEach { New-Item -Name $testfile -Path $testpath -ItemType "file" -Value "lorem ipsum" -Force - Test-Path $testfilepath | Should -BeTrue - } It "Should be able to be called on a regular file without error using the Path parameter" { @@ -138,4 +136,35 @@ Describe "Remove-Item" -Tags "CI" { Test-Path $testdirectory | Should -BeFalse } } + + Context "Alternate Data Streams should be supported on Windows" { + BeforeAll { + if (!$IsWindows) { + return + } + $fileName = "ADStest.txt" + $streamName = "teststream" + $dirName = "ADStestdir" + $fileContent =" This is file content." + $streamContent = "datastream content here" + $streamfile = Join-Path -Path $testpath -ChildPath $fileName + $streamdir = Join-Path -Path $testpath -ChildPath $dirName + + $null = New-Item -Path $streamfile -ItemType "File" -force + Add-Content -Path $streamfile -Value $fileContent + Add-Content -Path $streamfile -Stream $streamName -Value $streamContent + $null = New-Item -Path $streamdir -ItemType "Directory" -Force + Add-Content -Path $streamdir -Stream $streamName -Value $streamContent + } + It "Should completely remove a datastream from a file" -Skip:(!$IsWindows) { + Get-Item -Path $streamfile -Stream $streamName | Should -Not -BeNullOrEmpty + Remove-Item -Path $streamfile -Stream $streamName + Get-Item -Path $streamfile -Stream $streamName -ErrorAction SilentlyContinue | Should -BeNullOrEmpty + } + It "Should completely remove a datastream from a directory" -Skip:(!$IsWindows) { + Get-Item -Path $streamdir -Stream $streamName | Should -Not -BeNullOrEmpty + Remove-Item -Path $streamdir -Stream $streamName + Get-Item -Path $streamdir -Stream $streamname -ErrorAction SilentlyContinue | Should -BeNullOrEmpty + } + } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Content.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Content.Tests.ps1 index 6e8f8bde083..fefd73412df 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Content.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Content.Tests.ps1 @@ -72,6 +72,39 @@ Describe "Set-Content cmdlet tests" -Tags "CI" { $result[1] | Should -BeExactly "world" } } + Context "Set-Content should work with alternate data streams on Windows" { + BeforeAll { + if ( -Not $IsWindows ) + { + return + } + $altStreamPath = "$TESTDRIVE/altStream.txt" + $altStreamPath2 = "$TESTDRIVE/altStream2.txt" + $altStreamDirectory = "$TESTDRIVE/altstreamdir" + $altStreamDirectory2 = "$TESTDRIVE/altstream2dir" + $stringData = "test data" + $streamName = "test" + $absentStreamName = "noExist" + $item = New-Item -type file $altStreamPath + $altstreamdiritem = New-Item -type directory $altStreamDirectory + } + It "Should create a new data stream on a file" -Skip:(-Not $IsWindows) { + Set-Content -Path $altStreamPath -Stream $streamName -Value $stringData + Get-Content -Path $altStreamPath -Stream $streamName | Should -BeExactly $stringData + } + It "Should create a new data stream on a file using colon syntax" -Skip:(-Not $IsWindows) { + Set-Content -Path ${altStreamPath2}:${streamName} -Value $stringData + Get-Content -Path ${altStreamPath2} -Stream $streamName | Should -BeExactly $stringData + } + It "Should create a new data stream on a directory" -Skip:(-Not $IsWindows) { + Set-Content -Path $altStreamDirectory -Stream $streamName -Value $stringData + Get-Content -Path $altStreamDirectory -Stream $streamName | Should -BeExactly $stringData + } + It "Should create a new data stream on a directory using colon syntax" -Skip:(-Not $IsWindows) { + Set-Content -Path ${altStreamDirectory2}:${streamName} -Value $stringData + Get-Content -Path ${altStreamDirectory2} -Stream ${streamName} | Should -BeExactly $stringData + } + } } Describe "Set-Content should work for PSDrive with UNC path as root" -Tags @('CI', 'RequireAdminOnWindows') { From 86b8a6b0e16da5fb979bde5a4847cf807333260f Mon Sep 17 00:00:00 2001 From: Kyle H Date: Wed, 11 Nov 2020 02:27:58 -0600 Subject: [PATCH 2/3] Properly hang the if block. --- .../namespaces/FileSystemProvider.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index c6845f29dab..a9e5c704c22 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1320,7 +1320,9 @@ protected override void GetItem(string path) foreach (AlternateStreamData stream in AlternateDataStreamUtilities.GetStreams(result.FullName)) { if (!p.IsMatch(stream.Stream)) - { continue; } + { + continue; + } string outputPath = result.FullName + ":" + stream.Stream; // Alternate data streams can never be containers. From d2e58ce2edf76c3c0bb2918fd08d77af6df77c07 Mon Sep 17 00:00:00 2001 From: Kyle H Date: Thu, 3 Dec 2020 14:17:37 -0600 Subject: [PATCH 3/3] Applying suggestions that github's interface didn't --- .../namespaces/FileSystemProvider.cs | 5 +++-- .../Microsoft.PowerShell.Management/Add-Content.Tests.ps1 | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index a9e5c704c22..94322d649d1 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1326,7 +1326,7 @@ protected override void GetItem(string path) string outputPath = result.FullName + ":" + stream.Stream; // Alternate data streams can never be containers. - WriteItemObject(stream, outputPath, false); + WriteItemObject(stream, outputPath, isContainer: false); foundStream = true; } @@ -6971,7 +6971,7 @@ public void ClearContent(string path) // alternate data streams on them that are not child items. These alternate data streams // must be treated as data streams, even if they're attached to directories. However, // if asked to work with a directory without a data stream specified, write a non-terminating - // error instead of clearing all child items of the directory. (On non-Windows, alternate + // error instead of clearing all child items of the directory. (On non-Windows, alternate // data streams don't exist, so in that environment always write the error when addressing // a directory.) if (Directory.Exists(path) @@ -8650,6 +8650,7 @@ internal static List GetStreams(string path) SafeFindHandle handle = NativeMethods.FindFirstStreamW( path, NativeMethods.StreamInfoLevels.FindStreamInfoStandard, findStreamData, 0); + if (handle.IsInvalid) { int error = Marshal.GetLastWin32Error(); diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Add-Content.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Add-Content.Tests.ps1 index e601971ba87..6d63206b709 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Add-Content.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Add-Content.Tests.ps1 @@ -59,10 +59,12 @@ Describe "Add-Content cmdlet tests" -Tags "CI" { Setup -Directory "$ADSTestDir" Setup -File "$ADSTestFile" } + It "Should add an alternate data stream on a directory" -Skip:(!$IsWindows) { Add-Content -Path TestDrive:\$ADSTestDir -Stream Add-Content-Test-Stream -Value $streamContent -ErrorAction Stop Get-Content -Path TestDrive:\$ADSTestDir -Stream Add-Content-Test-Stream | Should -BeExactly $streamContent } + It "Should add an alternate data stream on a file" -Skip:(!$IsWindows) { Add-Content -Path TestDrive:\$ADSTestFile -Stream Add-Content-Test-Stream -Value $streamContent -ErrorAction Stop Get-Content -Path TestDrive:\$ADSTestFile -Stream Add-Content-Test-Stream | Should -BeExactly $streamContent