Skip to content

Commit 3eca7c0

Browse files
authored
Adds a .ResolvedTarget Property to File-System Items to Reflect a Symlink's Target as FileSystemInfo (PowerShell#16490)
1 parent 2c27229 commit 3eca7c0

File tree

4 files changed

+93
-1
lines changed

4 files changed

+93
-1
lines changed

src/System.Management.Automation/engine/TypeTable_Types_Ps1Xml.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,17 @@ private void Process_Types_Ps1Xml(string filePath, ConcurrentBag<string> errors)
676676
typeMembers,
677677
isOverride: false);
678678

679+
newMembers.Add(@"ResolvedTarget");
680+
AddMember(
681+
errors,
682+
typeName,
683+
new PSCodeProperty(
684+
@"ResolvedTarget",
685+
GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), @"ResolvedTarget"),
686+
setterCodeReference: null),
687+
typeMembers,
688+
isOverride: false);
689+
679690
newMembers.Add(@"Target");
680691
AddMember(
681692
errors,
@@ -801,6 +812,17 @@ private void Process_Types_Ps1Xml(string filePath, ConcurrentBag<string> errors)
801812
typeMembers,
802813
isOverride: false);
803814

815+
newMembers.Add(@"ResolvedTarget");
816+
AddMember(
817+
errors,
818+
typeName,
819+
new PSCodeProperty(
820+
@"ResolvedTarget",
821+
GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), @"ResolvedTarget"),
822+
setterCodeReference: null),
823+
typeMembers,
824+
isOverride: false);
825+
804826
newMembers.Add(@"Target");
805827
AddMember(
806828
errors,

src/System.Management.Automation/namespaces/FileSystemProvider.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8131,6 +8131,22 @@ public static string GetTarget(PSObject instance)
81318131
return null;
81328132
}
81338133

8134+
/// <summary>
8135+
/// Gets the target for a given file or directory, resolving symbolic links.
8136+
/// </summary>
8137+
/// <param name="instance">The FileInfo or DirectoryInfo type.</param>
8138+
/// <returns>The file path the instance points to.</returns>
8139+
public static string ResolvedTarget(PSObject instance)
8140+
{
8141+
if (instance.BaseObject is FileSystemInfo fileSysInfo)
8142+
{
8143+
FileSystemInfo linkTarget = fileSysInfo.ResolveLinkTarget(true);
8144+
return linkTarget is null ? fileSysInfo.FullName : linkTarget.FullName;
8145+
}
8146+
8147+
return null;
8148+
}
8149+
81348150
/// <summary>
81358151
/// Gets the link type of the specified reparse point.
81368152
/// </summary>

test/powershell/Modules/Microsoft.PowerShell.Management/FileSystemProviderExtended.Tests.ps1

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,3 +658,57 @@ Describe "FileSystem Provider Extended Tests for Get-ChildItem cmdlet" -Tags "CI
658658
}
659659
}
660660
}
661+
662+
Describe "Validate Get-Item ResolvedTarget property" -Tag RequireAdminOnWindows {
663+
BeforeAll {
664+
$rootDir = Join-Path "TestDrive:" "TestDir"
665+
666+
Push-Location $rootDir
667+
$null = New-Item -Path "realDir" -ItemType Directory
668+
$null = New-Item -Path "toDel" -ItemType Directory
669+
$null = New-Item -Path "brokenLinkedDir" -ItemType SymbolicLink -Value ".\toDel"
670+
$null = New-Item -Path "linkedDir" -ItemType SymbolicLink -Value ".\realDir"
671+
Remove-Item "toDel"
672+
$null = New-Item -Path "realFile.fil" -ItemType File
673+
$null = New-Item -Path "toDel.fil" -ItemType File
674+
$null = New-Item -Path "brokenLinkedFile.fil" -ItemType SymbolicLink -Value ".\toDel.fil"
675+
$null = New-Item -Path "linkedFile.fil" -ItemType SymbolicLink -Value ".\realFile.fil"
676+
Remove-Item "toDel.fil"
677+
}
678+
679+
AfterAll {
680+
Pop-Location
681+
}
682+
683+
Context 'Get-Item files and folders' {
684+
It 'Get-Item "linkedDir"' {
685+
$result = Get-Item "linkedDir"
686+
$result.ResolvedTarget.EndsWith("realDir") | Should -BeTrue
687+
}
688+
689+
It 'Get-Item "linkedFile.fil"' {
690+
$result = Get-Item "linkedFile.fil"
691+
$result.ResolvedTarget.EndsWith("realFile.fil") | Should -BeTrue
692+
}
693+
694+
It 'Get-Item "brokenLinkedDir"' {
695+
$result = Get-Item "brokenLinkedDir"
696+
$result.ResolvedTarget.EndsWith("toDel") | Should -BeTrue
697+
}
698+
699+
It 'Get-Item "brokenLinkedFile.fil"' {
700+
$result = Get-Item "brokenLinkedFile.fil"
701+
$result.ResolvedTarget.EndsWith("toDel.fil") | Should -BeTrue
702+
}
703+
704+
It 'Get-Item "realDir"' {
705+
$result = Get-Item "realDir"
706+
$result.ResolvedTarget.EndsWith("realDir") | Should -BeTrue
707+
}
708+
709+
It 'Get-Item "realFile.fil' {
710+
$result = Get-Item "realFile.fil"
711+
$result.ResolvedTarget.EndsWith("realFile.fil") | Should -BeTrue
712+
}
713+
}
714+
}

test/powershell/engine/Api/TypeInference.Tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ Describe "Type inference Tests" -tags "CI" {
504504
It "Infers typeof Select-Object when Parameter is ExcludeProperty" {
505505
$res = [AstTypeInference]::InferTypeOf( { [io.fileinfo]::new("file") | Select-Object -ExcludeProperty *Time*, E* }.Ast)
506506
$res.Count | Should -Be 1
507-
$res[0].Name | Should -BeExactly "System.Management.Automation.PSObject#Attributes:BaseName:Directory:DirectoryName:FullName:IsReadOnly:Length:LengthString:LinkTarget:LinkType:Mode:ModeWithoutHardLink:Name:NameString:Target:VersionInfo"
507+
$res[0].Name | Should -BeExactly "System.Management.Automation.PSObject#Attributes:BaseName:Directory:DirectoryName:FullName:IsReadOnly:Length:LengthString:LinkTarget:LinkType:Mode:ModeWithoutHardLink:Name:NameString:ResolvedTarget:Target:VersionInfo"
508508
$names = $res[0].Members.Name
509509
$names -contains "BaseName" | Should -BeTrue
510510
$names -contains "Name" | Should -BeTrue

0 commit comments

Comments
 (0)