Removing Hardlink from Mode property in default file system format (#8789)

This commit is contained in:
Staffan Gustafsson 2019-03-12 23:49:16 +01:00 committed by Aditya Patwardhan
parent 1e49d09d29
commit 9983297254
8 changed files with 207 additions and 143 deletions

View File

@ -42,28 +42,33 @@ namespace System.Management.Automation.Runspaces
private static IEnumerable<FormatViewDefinition> ViewsOf_FileSystemTypes(CustomControl[] sharedControls) private static IEnumerable<FormatViewDefinition> ViewsOf_FileSystemTypes(CustomControl[] sharedControls)
{ {
const string LengthScriptBlock =
@"if ($_ -is [System.IO.DirectoryInfo]) { return '' }
if ($_.Attributes -band [System.IO.FileAttributes]::Offline)
{
return '({0})' -f $_.Length
}
return $_.Length";
yield return new FormatViewDefinition("children", yield return new FormatViewDefinition("children",
TableControl.Create() TableControl.Create()
.GroupByProperty("PSParentPath", customControl: sharedControls[0]) .GroupByProperty("PSParentPath", customControl: sharedControls[0])
.AddHeader(Alignment.Left, label: "Mode", width: 7) .AddHeader(Alignment.Left, label: "Mode", width: 7)
.AddHeader(Alignment.Right, label: "LastWriteTime", width: 25) .AddHeader(Alignment.Right, label: "LastWriteTime", width: 25)
.AddHeader(Alignment.Right, label: "Length", width: 14) .AddHeader(Alignment.Right, label: "Length", width: 14)
.AddHeader() .AddHeader(Alignment.Left, label: "Name")
.StartRowDefinition(wrap: true)
.AddPropertyColumn("ModeWithoutHardLink")
.AddPropertyColumn("LastWriteTimeString")
.AddPropertyColumn("LengthString")
.AddPropertyColumn("NameString")
.EndRowDefinition()
.EndTable());
yield return new FormatViewDefinition("childrenWithHardlink",
TableControl.Create()
.GroupByProperty("PSParentPath", customControl: sharedControls[0])
.AddHeader(Alignment.Left, label: "Mode", width: 7)
.AddHeader(Alignment.Right, label: "LastWriteTime", width: 25)
.AddHeader(Alignment.Right, label: "Length", width: 14)
.AddHeader(Alignment.Left, label: "Name")
.StartRowDefinition(wrap: true) .StartRowDefinition(wrap: true)
.AddPropertyColumn("Mode") .AddPropertyColumn("Mode")
.AddScriptBlockColumn(@" .AddPropertyColumn("LastWriteTimeString")
[String]::Format(""{0,10} {1,8}"", $_.LastWriteTime.ToString(""d""), $_.LastWriteTime.ToString(""t"")) .AddPropertyColumn("LengthString")
") .AddPropertyColumn("NameString")
.AddScriptBlockColumn(LengthScriptBlock)
.AddPropertyColumn("Name")
.EndRowDefinition() .EndRowDefinition()
.EndTable()); .EndTable());
@ -72,7 +77,7 @@ return $_.Length";
.GroupByProperty("PSParentPath", customControl: sharedControls[0]) .GroupByProperty("PSParentPath", customControl: sharedControls[0])
.StartEntry(entrySelectedByType: new[] { "System.IO.FileInfo" }) .StartEntry(entrySelectedByType: new[] { "System.IO.FileInfo" })
.AddItemProperty(@"Name") .AddItemProperty(@"Name")
.AddItemScriptBlock(LengthScriptBlock, label: "Length") .AddItemProperty("LengthString", label: "Length")
.AddItemProperty(@"CreationTime") .AddItemProperty(@"CreationTime")
.AddItemProperty(@"LastWriteTime") .AddItemProperty(@"LastWriteTime")
.AddItemProperty(@"LastAccessTime") .AddItemProperty(@"LastAccessTime")

View File

@ -183,18 +183,28 @@ namespace System.Management.Automation.Runspaces
var td19 = new TypeData(@"System.IO.DirectoryInfo", true); var td19 = new TypeData(@"System.IO.DirectoryInfo", true);
td19.Members.Add("Mode", td19.Members.Add("Mode",
new CodePropertyData("Mode", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "Mode"), null)); new CodePropertyData("Mode", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "Mode"), null));
td19.Members.Add("ModeWithoutHardLink",
new CodePropertyData("ModeWithoutHardLink", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "ModeWithoutHardLink"), null));
td19.Members.Add("BaseName", td19.Members.Add("BaseName",
new ScriptPropertyData(@"BaseName", GetScriptBlock(@"$this.Name"), null)); new ScriptPropertyData(@"BaseName", GetScriptBlock(@"$this.Name"), null));
td19.Members.Add("Target", td19.Members.Add("Target",
new CodePropertyData("Target", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), "GetTarget"), null)); new CodePropertyData("Target", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), "GetTarget"), null));
td19.Members.Add("LinkType", td19.Members.Add("LinkType",
new CodePropertyData("LinkType", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), "GetLinkType"), null)); new CodePropertyData("LinkType", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), "GetLinkType"), null));
td19.Members.Add("NameString",
new CodePropertyData("NameString", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "NameString"), null) { IsHidden = true });
td19.Members.Add("LengthString",
new CodePropertyData("LengthString", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "LengthString"), null) { IsHidden = true });
td19.Members.Add("LastWriteTimeString",
new CodePropertyData("LastWriteTimeString", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "LastWriteTimeString"), null) { IsHidden = true });
td19.DefaultDisplayProperty = @"Name"; td19.DefaultDisplayProperty = @"Name";
yield return td19; yield return td19;
var td20 = new TypeData(@"System.IO.FileInfo", true); var td20 = new TypeData(@"System.IO.FileInfo", true);
td20.Members.Add("Mode", td20.Members.Add("Mode",
new CodePropertyData("Mode", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "Mode"), null)); new CodePropertyData("Mode", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "Mode"), null));
td20.Members.Add("ModeWithoutHardLink",
new CodePropertyData("ModeWithoutHardLink", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "ModeWithoutHardLink"), null));
td20.Members.Add("VersionInfo", td20.Members.Add("VersionInfo",
new ScriptPropertyData(@"VersionInfo", GetScriptBlock(@"[System.Diagnostics.FileVersionInfo]::GetVersionInfo($this.FullName)"), null)); new ScriptPropertyData(@"VersionInfo", GetScriptBlock(@"[System.Diagnostics.FileVersionInfo]::GetVersionInfo($this.FullName)"), null));
td20.Members.Add("BaseName", td20.Members.Add("BaseName",
@ -203,6 +213,12 @@ namespace System.Management.Automation.Runspaces
new CodePropertyData("Target", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), "GetTarget"), null)); new CodePropertyData("Target", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), "GetTarget"), null));
td20.Members.Add("LinkType", td20.Members.Add("LinkType",
new CodePropertyData("LinkType", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), "GetLinkType"), null)); new CodePropertyData("LinkType", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), "GetLinkType"), null));
td20.Members.Add("NameString",
new CodePropertyData("NameString", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "NameString"), null) { IsHidden = true });
td20.Members.Add("LengthString",
new CodePropertyData("LengthString", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "LengthString"), null) { IsHidden = true });
td20.Members.Add("LastWriteTimeString",
new CodePropertyData("LastWriteTimeString", GetMethodInfo(typeof(Microsoft.PowerShell.Commands.FileSystemProvider), "LastWriteTimeString"), null) { IsHidden = true });
td20.DefaultDisplayPropertySet = td20.DefaultDisplayPropertySet =
new PropertySetData(new[] { "LastWriteTime", "Length", "Name" }) { Name = "DefaultDisplayPropertySet" }; new PropertySetData(new[] { "LastWriteTime", "Length", "Name" }) { Name = "DefaultDisplayPropertySet" };
yield return td20; yield return td20;

View File

@ -1867,33 +1867,99 @@ namespace Microsoft.PowerShell.Commands
/// Provides a mode property for FileSystemInfo. /// Provides a mode property for FileSystemInfo.
/// </summary> /// </summary>
/// <param name="instance">Instance of PSObject wrapping a FileSystemInfo.</param> /// <param name="instance">Instance of PSObject wrapping a FileSystemInfo.</param>
/// <returns>A string representation of the FileAttributes, with one letter per attribute.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
public static string Mode(PSObject instance) public static string Mode(PSObject instance) => Mode(instance, excludeHardLink: false);
/// <summary>
/// Provides a ModeWithoutHardLink property for FileSystemInfo, without HardLinks for performance reasons.
/// </summary>
/// <param name="instance">Instance of PSObject wrapping a FileSystemInfo.</param>
/// <returns>A string representation of the FileAttributes, with one letter per attribute.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
public static string ModeWithoutHardLink(PSObject instance) => Mode(instance, excludeHardLink: true);
private static string Mode(PSObject instance, bool excludeHardLink)
{ {
if (instance == null) string ToModeString(FileSystemInfo fileSystemInfo)
{ {
return string.Empty; FileAttributes fileAttributes = fileSystemInfo.Attributes;
bool isReparsePoint = InternalSymbolicLinkLinkCodeMethods.IsReparsePoint(fileSystemInfo);
bool isLink = isReparsePoint || (excludeHardLink ? false : InternalSymbolicLinkLinkCodeMethods.IsHardLink(fileSystemInfo));
if (!isLink)
{
// special casing for the common cases - no allocations
switch (fileAttributes)
{
case FileAttributes.Archive:
return "-a---";
case FileAttributes.Directory:
return "d----";
case FileAttributes.Normal:
return "-----";
case FileAttributes.Directory | FileAttributes.ReadOnly:
return "d-r--";
case FileAttributes.Archive | FileAttributes.ReadOnly:
return "-ar--";
}
}
bool isDirectory = fileAttributes.HasFlag(FileAttributes.Directory);
ReadOnlySpan<char> mode = stackalloc char[]
{
isLink ? 'l' : isDirectory ? 'd' : '-',
fileAttributes.HasFlag(FileAttributes.Archive) ? 'a' : '-',
fileAttributes.HasFlag(FileAttributes.ReadOnly) ? 'r' : '-',
fileAttributes.HasFlag(FileAttributes.Hidden) ? 'h' : '-',
fileAttributes.HasFlag(FileAttributes.System) ? 's' : '-',
};
return new string(mode);
} }
FileSystemInfo fileInfo = (FileSystemInfo)instance.BaseObject; return instance?.BaseObject is FileSystemInfo fileInfo
if (fileInfo == null) ? ToModeString(fileInfo)
{ : string.Empty;
return string.Empty; }
}
char[] mode = new char[6]; /// <summary>
mode[0] = (fileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory ? 'd' : '-'; /// Provides a NameString property for FileSystemInfo.
mode[1] = (fileInfo.Attributes & FileAttributes.Archive) == FileAttributes.Archive ? 'a' : '-'; /// </summary>
mode[2] = (fileInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly ? 'r' : '-'; /// <param name="instance">Instance of PSObject wrapping a FileSystemInfo.</param>
mode[3] = (fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden ? 'h' : '-'; /// <returns>Name if a file or directory, Name -> Target if symlink.</returns>
mode[4] = (fileInfo.Attributes & FileAttributes.System) == FileAttributes.System ? 's' : '-'; public static string NameString(PSObject instance)
// Mark the last bit as a "l" if it's a reparsepoint (symbolic link or junction) {
// Porting note: these need to be handled specially return instance?.BaseObject is FileSystemInfo fileInfo
bool isReparsePoint = InternalSymbolicLinkLinkCodeMethods.IsReparsePoint(fileInfo); ? InternalSymbolicLinkLinkCodeMethods.IsReparsePoint(fileInfo)
bool isHardLink = InternalSymbolicLinkLinkCodeMethods.IsHardLink(fileInfo); ? $"{fileInfo.Name} -> {InternalSymbolicLinkLinkCodeMethods.GetTarget(instance)}"
mode[5] = isReparsePoint || isHardLink ? 'l' : '-'; : fileInfo.Name
: string.Empty;
}
return new string(mode); /// <summary>
/// Provides a LengthString property for FileSystemInfo.
/// </summary>
/// <param name="instance">Instance of PSObject wrapping a FileSystemInfo.</param>
/// <returns>Length as a string.</returns>
public static string LengthString(PSObject instance)
{
return instance?.BaseObject is FileInfo fileInfo
? fileInfo.Attributes.HasFlag(FileAttributes.Offline)
? $"({fileInfo.Length})"
: fileInfo.Length.ToString()
: string.Empty;
}
/// <summary>
/// Provides a LastWriteTimeString property for FileSystemInfo.
/// </summary>
/// <param name="instance">Instance of PSObject wrapping a FileSystemInfo.</param>
/// <returns>LastWriteTime formatted as short date + short time.</returns>
public static string LastWriteTimeString(PSObject instance)
{
return instance?.BaseObject is FileSystemInfo fileInfo
? string.Format(CultureInfo.CurrentCulture, "{0,10:d} {0,8:t}", fileInfo.LastWriteTime)
: string.Empty;
} }
#region RenameItem #region RenameItem
@ -2481,8 +2547,8 @@ namespace Microsoft.PowerShell.Commands
flags |= NativeMethods.SymbolicLinkFlags.AllowUnprivilegedCreate; flags |= NativeMethods.SymbolicLinkFlags.AllowUnprivilegedCreate;
} }
int created = NativeMethods.CreateSymbolicLink(path, strTargetPath, flags); var created = NativeMethods.CreateSymbolicLink(path, strTargetPath, flags);
return (created == 1) ? true : false; return created;
} }
private static bool WinCreateHardLink(string path, string strTargetPath) private static bool WinCreateHardLink(string path, string strTargetPath)
@ -7013,7 +7079,8 @@ namespace Microsoft.PowerShell.Commands
/// <param name="symbolicLinkFlags">Flag values from SymbolicLinkFlags enum.</param> /// <param name="symbolicLinkFlags">Flag values from SymbolicLinkFlags enum.</param>
/// <returns>1 on successful creation.</returns> /// <returns>1 on successful creation.</returns>
[DllImport(PinvokeDllNames.CreateSymbolicLinkDllName, CharSet = CharSet.Unicode, SetLastError = true)] [DllImport(PinvokeDllNames.CreateSymbolicLinkDllName, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int CreateSymbolicLink(string name, string destination, SymbolicLinkFlags symbolicLinkFlags); [return: MarshalAs(UnmanagedType.I1)]
internal static extern bool CreateSymbolicLink(string name, string destination, SymbolicLinkFlags symbolicLinkFlags);
/// <summary> /// <summary>
/// Flags used when creating a symbolic link. /// Flags used when creating a symbolic link.
@ -7034,7 +7101,7 @@ namespace Microsoft.PowerShell.Commands
/// <summary> /// <summary>
/// Allow creation of symbolic link without elevation. Requires Developer mode. /// Allow creation of symbolic link without elevation. Requires Developer mode.
/// </summary> /// </summary>
AllowUnprivilegedCreate = 2 AllowUnprivilegedCreate = 2,
} }
/// <summary> /// <summary>
@ -7709,7 +7776,7 @@ namespace Microsoft.PowerShell.Commands
/// </summary> /// </summary>
/// <param name="instance">The object of FileInfo or DirectoryInfo type.</param> /// <param name="instance">The object of FileInfo or DirectoryInfo type.</param>
/// <returns>The target of the reparse point.</returns> /// <returns>The target of the reparse point.</returns>
public static IEnumerable<string> GetTarget(PSObject instance) public static string GetTarget(PSObject instance)
{ {
if (instance.BaseObject is FileSystemInfo fileSysInfo) if (instance.BaseObject is FileSystemInfo fileSysInfo)
{ {
@ -7718,13 +7785,11 @@ namespace Microsoft.PowerShell.Commands
{ {
string linkTarget = WinInternalGetTarget(handle); string linkTarget = WinInternalGetTarget(handle);
if (linkTarget != null) return linkTarget;
{
return (new string[] { linkTarget });
}
} }
#else
return UnixInternalGetTarget(fileSysInfo.FullName);
#endif #endif
return InternalGetTarget(fileSysInfo.FullName);
} }
return null; return null;
@ -7743,84 +7808,23 @@ namespace Microsoft.PowerShell.Commands
{ {
return InternalGetLinkType(fileSysInfo); return InternalGetLinkType(fileSysInfo);
} }
else
return null; return null;
} }
private static List<string> InternalGetTarget(string filePath)
{
var links = new List<string>();
#if UNIX #if UNIX
private static string UnixInternalGetTarget(string filePath)
{
string link = Platform.NonWindowsInternalGetTarget(filePath); string link = Platform.NonWindowsInternalGetTarget(filePath);
if (!string.IsNullOrEmpty(link))
{ if (string.IsNullOrEmpty(link))
links.Add(link);
}
else
{ {
throw new Win32Exception(Marshal.GetLastWin32Error()); throw new Win32Exception(Marshal.GetLastWin32Error());
} }
#elif !CORECLR // FindFirstFileName, FindNextFileName and FindClose are not available on Core Clr return link;
UInt32 linkStringLength = 0;
var linkName = new StringBuilder();
// First get the length for the linkName buffer.
IntPtr fileHandle = InternalSymbolicLinkLinkCodeMethods.FindFirstFileName(filePath, 0, ref linkStringLength, linkName);
int lastError = Marshal.GetLastWin32Error();
// Return handle is INVALID_HANDLE_VALUE and LastError was ERROR_MORE_DATA
if ((fileHandle == (IntPtr)(-1)) && (lastError == 234))
{
linkName = new StringBuilder((int)linkStringLength);
fileHandle = InternalSymbolicLinkLinkCodeMethods.FindFirstFileName(filePath, 0, ref linkStringLength, linkName);
lastError = Marshal.GetLastWin32Error();
}
if (fileHandle == (IntPtr)(-1))
{
throw new Win32Exception(lastError);
}
bool continueFind = false;
try
{
do
{
StringBuilder fullName = new StringBuilder();
fullName.Append(Path.GetPathRoot(filePath)); // hard link source and target must be on the same drive. So we can use the source for find the path root.
fullName.Append(linkName.ToString());
FileInfo fInfo = new FileInfo(fullName.ToString());
// Don't add the target link to the list.
if (string.Compare(fInfo.FullName, filePath, StringComparison.OrdinalIgnoreCase) != 0)
links.Add(fInfo.FullName);
continueFind = InternalSymbolicLinkLinkCodeMethods.FindNextFileName(fileHandle, ref linkStringLength, linkName);
lastError = Marshal.GetLastWin32Error();
if (!continueFind && lastError == 234) // ERROR_MORE_DATA
{
linkName = new StringBuilder((int)linkStringLength);
continueFind = InternalSymbolicLinkLinkCodeMethods.FindNextFileName(fileHandle, ref linkStringLength, linkName);
}
if (!continueFind && lastError != 38) // ERROR_HANDLE_EOF. No more links.
{
throw new Win32Exception(lastError);
}
}
while (continueFind);
}
finally
{
InternalSymbolicLinkLinkCodeMethods.FindClose(fileHandle);
}
#endif
return links;
} }
#endif
private static string InternalGetLinkType(FileSystemInfo fileInfo) private static string InternalGetLinkType(FileSystemInfo fileInfo)
{ {
@ -7909,16 +7913,9 @@ namespace Microsoft.PowerShell.Commands
internal static bool IsReparsePoint(FileSystemInfo fileInfo) internal static bool IsReparsePoint(FileSystemInfo fileInfo)
{ {
if (Platform.IsWindows) return Platform.IsWindows
{ ? fileInfo.Attributes.HasFlag(System.IO.FileAttributes.ReparsePoint)
// Note that this class also has a enum called FileAttributes, so use fully qualified name : Platform.NonWindowsIsSymLink(fileInfo);
return (fileInfo.Attributes & System.IO.FileAttributes.ReparsePoint)
== System.IO.FileAttributes.ReparsePoint;
}
else
{
return Platform.NonWindowsIsSymLink(fileInfo);
}
} }
internal static bool WinIsHardLink(FileSystemInfo fileInfo) internal static bool WinIsHardLink(FileSystemInfo fileInfo)

View File

@ -10,5 +10,10 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsWindows Condition="'$(OS)' == 'Windows_NT'">true</IsWindows>
</PropertyGroup>
<PropertyGroup Condition=" '$(IsWindows)' != 'true' ">
<DefineConstants>$(DefineConstants);UNIX</DefineConstants>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -190,3 +190,51 @@ Describe "Get-ChildItem" -Tags "CI" {
} }
} }
} }
Describe 'FileSystem Provider Formatting' -Tag "CI","RequireAdminOnWindows" {
BeforeAll {
$modeTestDir = New-Item -Path "$TestDrive/testmodedirectory" -ItemType Directory -Force
$targetFile1 = New-Item -Path "$TestDrive/targetFile1" -ItemType File -Force
$targetFile2 = New-Item -Path "$TestDrive/targetFile2" -ItemType File -Force
$targetDir1 = New-Item -Path "$TestDrive/targetDir1" -ItemType Directory -Force
$targetDir2 = New-Item -Path "$TestDrive/targetDir2" -ItemType Directory -Force
$testcases = @(
@{ expectedMode = "d----"; expectedModeWithoutHardlink = "d----"; itemType = "Directory"; itemName = "Directory"; fileAttributes = [System.IO.FileAttributes] "Directory"; target = $null }
@{ expectedMode = "l----"; expectedModeWithoutHardlink = "l----"; itemType = "SymbolicLink"; itemName = "SymbolicLink-Directory"; fileAttributes = [System.IO.FileAttributes]::Directory -bor [System.IO.FileAttributes]::ReparsePoint; target = $targetDir2.FullName }
)
if ($IsWindows)
{
$testcases += @{ expectedMode = "l----"; expectedModeWithoutHardlink = "l----"; itemType = "Junction"; itemName = "Junction-Directory"; fileAttributes = [System.IO.FileAttributes]::Directory -bor [System.IO.FileAttributes]::ReparsePoint; target = $targetDir1.FullName }
$testcases += @{ expectedMode = "-a---"; expectedModeWithoutHardlink = "-a---"; itemType = "File"; itemName = "ArchiveFile"; fileAttributes = [System.IO.FileAttributes] "Archive"; target = $null }
$testcases += @{ expectedMode = "la---"; expectedModeWithoutHardlink = "la---"; itemType = "SymbolicLink"; itemName = "SymbolicLink-File"; fileAttributes = [System.IO.FileAttributes]::Archive -bor [System.IO.FileAttributes]::ReparsePoint; target = $targetFile1.FullName }
$testcases += @{ expectedMode = "la---"; expectedModeWithoutHardlink = "-a---"; itemType = "HardLink"; itemName = "HardLink"; fileAttributes = [System.IO.FileAttributes] "Archive"; target = $targetFile2.FullName }
}
}
It 'Validate Mode property - <itemName>' -TestCases $testcases {
param($expectedMode, $expectedModeWithoutHardlink, $itemType, $itemName, $fileAttributes, $target)
$item = if ($target)
{
New-Item -Path $modeTestDir -Name $itemName -ItemType $itemType -Target $target
}
else
{
New-Item -Path $modeTestDir -Name $itemName -ItemType $itemType
}
$item | Should -BeOfType "System.IO.FileSystemInfo"
$actualMode = [Microsoft.PowerShell.Commands.FileSystemProvider]::Mode($item)
$actualMode | Should -BeExactly $expectedMode
$actualModeWithoutHardlink = [Microsoft.PowerShell.Commands.FileSystemProvider]::ModeWithoutHardlink($item)
$actualModeWithoutHardlink | Should -BeExactly $expectedModeWithoutHardlink
$item.Attributes | Should -Be $fileAttributes
}
}

View File

@ -15,7 +15,7 @@ Describe "Get-FormatData" -Tags "CI" {
$format.TypeNames | Should -HaveCount 2 $format.TypeNames | Should -HaveCount 2
$format.TypeNames[0] | Should -BeExactly "System.IO.DirectoryInfo" $format.TypeNames[0] | Should -BeExactly "System.IO.DirectoryInfo"
$format.TypeNames[1] | Should -BeExactly "System.IO.FileInfo" $format.TypeNames[1] | Should -BeExactly "System.IO.FileInfo"
$format.FormatViewDefinition | Should -HaveCount 3 $format.FormatViewDefinition | Should -HaveCount 4
} }
It "Should return nothing for format data requiring '-PowerShellVersion 5.1' and not provided" { It "Should return nothing for format data requiring '-PowerShellVersion 5.1' and not provided" {

View File

@ -418,7 +418,7 @@ Describe "Type inference Tests" -tags "CI" {
It "Infers typeof Select-Object when Parameter is ExcludeProperty" { It "Infers typeof Select-Object when Parameter is ExcludeProperty" {
$res = [AstTypeInference]::InferTypeOf( { [io.fileinfo]::new("file") | Select-Object -ExcludeProperty *Time*, E* }.Ast) $res = [AstTypeInference]::InferTypeOf( { [io.fileinfo]::new("file") | Select-Object -ExcludeProperty *Time*, E* }.Ast)
$res.Count | Should -Be 1 $res.Count | Should -Be 1
$res[0].Name | Should -Be "System.Management.Automation.PSObject#Attributes:BaseName:Directory:DirectoryName:FullName:IsReadOnly:Length:LinkType:Mode:Name:Target:VersionInfo" $res[0].Name | Should -BeExactly "System.Management.Automation.PSObject#Attributes:BaseName:Directory:DirectoryName:FullName:IsReadOnly:Length:LengthString:LinkType:Mode:ModeWithoutHardLink:Name:NameString:Target:VersionInfo"
$names = $res[0].Members.Name $names = $res[0].Members.Name
$names -contains "BaseName" | Should -BeTrue $names -contains "BaseName" | Should -BeTrue
$names -contains "Name" | Should -BeTrue $names -contains "Name" | Should -BeTrue

View File

@ -14,6 +14,8 @@ using System.Management.Automation.Internal.Host;
using System.Management.Automation.Provider; using System.Management.Automation.Provider;
using System.Management.Automation.Runspaces; using System.Management.Automation.Runspaces;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.PowerShell; using Microsoft.PowerShell;
using Microsoft.PowerShell.Commands; using Microsoft.PowerShell.Commands;
using Xunit; using Xunit;
@ -107,9 +109,9 @@ namespace PSTests.Parallel
executableObject = new FileInfo(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); executableObject = new FileInfo(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
} }
Assert.Equal("d-----", FileSystemProvider.Mode(PSObject.AsPSObject(directoryObject)).Replace("r", "-")); Assert.Equal("d----", FileSystemProvider.Mode(PSObject.AsPSObject(directoryObject)).Replace("r", "-"));
Assert.Equal("------", FileSystemProvider.Mode(PSObject.AsPSObject(fileObject)).Replace("r", "-").Replace("a", "-")); Assert.Equal("-----", FileSystemProvider.Mode(PSObject.AsPSObject(fileObject)).Replace("r", "-").Replace("a", "-"));
Assert.Equal("------", FileSystemProvider.Mode(PSObject.AsPSObject(executableObject)).Replace("r", "-").Replace("a", "-")); Assert.Equal("-----", FileSystemProvider.Mode(PSObject.AsPSObject(executableObject)).Replace("r", "-").Replace("a", "-"));
} }
[Fact] [Fact]
@ -125,13 +127,8 @@ namespace PSTests.Parallel
fileSystemProvider.GetProperty(testPath, new Collection<string>(){ "IsReadOnly" }); fileSystemProvider.GetProperty(testPath, new Collection<string>(){ "IsReadOnly" });
FileInfo fileSystemObject1 = new FileInfo(testPath); FileInfo fileSystemObject1 = new FileInfo(testPath);
PSObject psobject1 = PSObject.AsPSObject(fileSystemObject1); PSObject psobject1 = PSObject.AsPSObject(fileSystemObject1);
foreach (PSPropertyInfo property in psobject1.Properties) PSPropertyInfo property = psobject1.Properties["IsReadOnly"];
{ Assert.False((bool)property.Value);
if (property.Name == "IsReadOnly")
{
Assert.False((bool)property.Value);
}
}
} }
[Fact] [Fact]
@ -144,13 +141,9 @@ namespace PSTests.Parallel
fileSystemProvider.GetProperty(testPath, new Collection<string>(){ "Name" }); fileSystemProvider.GetProperty(testPath, new Collection<string>(){ "Name" });
FileInfo fileSystemObject1 = new FileInfo(testPath); FileInfo fileSystemObject1 = new FileInfo(testPath);
PSObject psobject1 = PSObject.AsPSObject(fileSystemObject1); PSObject psobject1 = PSObject.AsPSObject(fileSystemObject1);
foreach (PSPropertyInfo property in psobject1.Properties) PSPropertyInfo property = psobject1.Properties["FullName"];
{
if (property.Name == "FullName") Assert.Equal(testPath, property.Value);
{
Assert.Equal(testPath, property.Value);
}
}
} }
[Fact] [Fact]