Cleanup FileSystem provider (#6909)

The cleanup is coming from a code review to cleanup psl. Here we clean up the side branch of the code that will allow later to clean up a branch which uses psl.
- IO.FileInfo does not make system calls in constructor. So we can create the object and then use the required attributes without direct call IO.FileInfo.GetAttributes() ( SafeGetFileAttributes() ). This allow us to exclude some p/invoke calls in our code in later cleanups. Also we get unified code for both Windows and Unix.
- Remove SafeGetFileAttributes() and WinSafeGetFileAttributes(). Currently .Net Core support file attributes on all platforms in fastest way and we can remove our workaround. We get a regression in rare case (for files like pagefile.sys). Fix is ready in CoreFX, we get it in 2.1.1. I suggest ignore the regression because this is a very-very rare situation (Get-ChildItem c:\pagefile.sys -Hidden). The .Net Core team was not even able to create an artificial test for such files and uses a real pagefile.sys file for the test. Also the enumeration is still working (dir c:\ -hidden).
- Re-add test which we lost in #4050. The test is pending because of the regression.
This commit is contained in:
Ilya 2018-06-04 10:00:56 +05:00 committed by GitHub
parent 1f918bb648
commit 57b3d3380b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 112 deletions

View file

@ -1216,16 +1216,14 @@ namespace Microsoft.PowerShell.Commands
private FileSystemInfo GetFileSystemItem(string path, ref bool isContainer, bool showHidden)
{
path = NormalizePath(path);
FileInfo result = new FileInfo(path);
FileSystemInfo result = null;
// First see if the path is to a file by
// constructing a FileInfo object
int attribs = SafeGetFileAttributes(path);
bool exists = (attribs != -1);
bool directory = (attribs & ((int)NativeMethods.FileAttributes.Directory)) == ((int)NativeMethods.FileAttributes.Directory);
bool hidden = (attribs & ((int)NativeMethods.FileAttributes.Hidden)) == ((int)NativeMethods.FileAttributes.Hidden);
var attributes = result.Attributes;
// FileInfo.Exists is true for files but false for directories
// so we check attributes directly.
bool exists = (int)attributes != -1;
bool hidden = attributes.HasFlag(FileAttributes.Hidden);
isContainer = attributes.HasFlag(FileAttributes.Directory);
FlagsExpression<FileAttributes> evaluator = null;
FlagsExpression<FileAttributes> switchEvaluator = null;
@ -1250,47 +1248,36 @@ namespace Microsoft.PowerShell.Commands
// if "Hidden" is specified in the attribute filter dynamic parameters
// also return the object
if (exists && !directory && (!hidden || Force || showHidden || filterHidden || switchFilterHidden))
if (!isContainer)
{
FileInfo fileObj = new FileInfo(path);
if (exists && (!hidden || Force || showHidden || filterHidden || switchFilterHidden))
{
s_tracer.WriteLine("Got file info: {0}", result);
result = fileObj;
s_tracer.WriteLine("Got FileInfo: {0}", fileObj);
return result;
}
}
else
{
// if its not a file, maybe its a directory
DirectoryInfo directoryObj =
new DirectoryInfo(path);
// Check to see if the path is the root of a file system drive.
// Since all root paths are hidden we need to show the directory
// anyway
bool isRootPath =
String.Compare(
Path.GetPathRoot(path),
directoryObj.FullName,
result.FullName,
StringComparison.OrdinalIgnoreCase) == 0;
// if "Hidden" is specified in the attribute filter dynamic parameters
// also return the object
if (exists && (isRootPath || !hidden || Force || showHidden || filterHidden || switchFilterHidden))
{
Dbg.Diagnostics.Assert(
(directoryObj.Attributes &
FileAttributes.Directory) ==
FileAttributes.Directory,
"The object is not a directory?");
s_tracer.WriteLine("Got directory info: {0}", result);
result = directoryObj;
isContainer = true;
s_tracer.WriteLine("Got DirectoryInfo: {0}", directoryObj);
return new DirectoryInfo(path);
}
}
return result;
return null;
} // GetFileSystemItem
/// <summary>
@ -7013,88 +7000,6 @@ namespace Microsoft.PowerShell.Commands
#endregion IContentCmdletProvider
internal static int SafeGetFileAttributes(string path)
{
#if UNIX
System.IO.FileAttributes attr = System.IO.File.GetAttributes(path);
int result = 0;
if ((attr & FileAttributes.Archive) == FileAttributes.Archive)
result |= 0x20;
if ((attr & FileAttributes.Compressed) == FileAttributes.Compressed)
result |= 0x800;
if ((attr & FileAttributes.Device) == FileAttributes.Device)
result |= 0x40;
if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
result |= 0x10;
if ((attr & FileAttributes.Encrypted) == FileAttributes.Encrypted)
result |= 0x4000;
if ((attr & FileAttributes.Hidden) == FileAttributes.Hidden)
result |= 0x2;
if ((attr & FileAttributes.IntegrityStream) == FileAttributes.IntegrityStream)
result |= 0x8000;
if ((attr & FileAttributes.Normal) == FileAttributes.Normal)
result |= 0x80;
if ((attr & FileAttributes.NoScrubData) == FileAttributes.NoScrubData)
result |= 0x20000;
if ((attr & FileAttributes.NotContentIndexed) == FileAttributes.NotContentIndexed)
result |= 0x2000;
if ((attr & FileAttributes.Offline) == FileAttributes.Offline)
result |= 0x1000;
if ((attr & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
result |= 0x1;
if ((attr & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
result |= 0x400;
if ((attr & FileAttributes.SparseFile) == FileAttributes.SparseFile)
result |= 0x200;
if ((attr & FileAttributes.System) == FileAttributes.System)
result |= 0x4;
if ((attr & FileAttributes.Temporary) == FileAttributes.Temporary)
result |= 0x100;
return result;
#else
return WinSafeGetFileAttributes(path);
#endif
}
internal static int WinSafeGetFileAttributes(string path)
{
int result = Utils.NativeMethods.GetFileAttributes(path);
if (result == -1)
{
int errorCode = Marshal.GetLastWin32Error();
if (errorCode == 5)
{
// Handle "Access denied" specifically.
Win32Exception win32Exception = new Win32Exception(errorCode);
throw new UnauthorizedAccessException(win32Exception.Message, win32Exception);
}
else if (errorCode == 32)
{
// Errorcode 32 is 'ERROR_SHARING_VIOLATION' i.e.
// The process cannot access the file because it is being used by another process.
// GetFileAttributes may return INVALID_FILE_ATTRIBUTES for a system file or directory because of this error.
// GetFileAttributes function tries to open the file with FILE_READ_ATTRIBUTES access right but it fails if the
// sharing flag for the file is set to 0x00000000.This flag prevents it from opening a file for delete, read, or
// write access. For example: C:\pagefile.sys is always opened by OS with sharing flag 0x00000000.
// But FindFirstFile is still able to get attributes as this api retrieves the required information using a find
// handle generated with FILE_LIST_DIRECTORY access.
// Fall back to FindFirstFile to check if the file actually exists.
IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
Utils.NativeMethods.WIN32_FIND_DATA findData;
IntPtr findHandle = Utils.NativeMethods.FindFirstFile(path, out findData);
if (findHandle != INVALID_HANDLE_VALUE)
{
Utils.NativeMethods.FindClose(findHandle);
return (int)findData.dwFileAttributes;
}
}
}
return result;
}
/// <summary>
/// -raw is not allowed when -first,-last or -wait is specified
/// this call will validate that and throws.

View file

@ -120,6 +120,18 @@ Describe "Get-ChildItem" -Tags "CI" {
(Get-ChildItem -Path $searchRoot -File -Recurse).Count | Should -Be 1
(Get-ChildItem -Path $searchRoot -Directory -Recurse).Count | Should -Be 1
}
It "Should give .sys file if the fullpath is specified with hidden and force parameter" -Pending:$true {
# Enable the test after move to .Net Core 2.1.1
# The tracking issue https://github.com/dotnet/corefx/issues/29782
#
#It "Should give .sys file if the fullpath is specified with hidden and force parameter" -Skip:(!$IsWindows){
# Don't remove!!! It is special test for hidden and opened file with exclusive lock.
$file = Get-ChildItem -path "$env:SystemDrive\\pagefile.sys" -Hidden
$file | Should not be $null
$file.Count | Should be 1
$file.Name | Should be "pagefile.sys"
}
}
Context 'Env: Provider' {