-
Notifications
You must be signed in to change notification settings - Fork 7.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cleanup NativeItemExists() #6929
Conversation
123c38a
to
0b84586
Compare
Reopen to restart CIs. |
@TravisEz13 Could you please help - I can not find why CIs failed? |
Feature tests failed with Pester not found. 😕 |
catch | ||
{ | ||
} | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be
return result;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
bool result = false; | ||
try | ||
{ | ||
result = ItemExists(path, out bool unused); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extra whitespace after equals sign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps use named parameter to make it more clear what unused
is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused
here is self explaination - we don't use the parameter at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra space fixed.
} | ||
|
||
// This is done through P/Invoke since File.Exists and Directory.Exists pay 13% performance degradation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we sure we no longer have a perf penalty with this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have still a perf penalty (on windows because normalization(!) - one extra kernel call in .Net code) but it is tracked in CoreFX https://github.com/dotnet/corefx/issues/19717
We have already mentioned that we do up to 7 the normalizations of the same path per cmdlet call. So perf penalty is more in PowerShell code. I'm trying to find how we can reduce the path normalization perf penalty.
Also please see https://github.com/dotnet/corefx/issues/27049#issuecomment-392826352 - .Net team is ready to help.
The Linux failure does not seem related to your change |
NativeItemExists -> ItemExists NativeFileExists -> FileExists NativeDirectoryExists -> DirectoryExists
8e8ac45
to
67c5d33
Compare
@TravisEz13 Thanks! But CIs failed again. I rebased - perhaps it resolve.. |
No need in throw here if file not found. (Previously we could throw in rare cases.)
@SteveL-MSFT @TravisEz13 Please review last two commits. I found my mistake. I added new comment and TODO to continue the cleanup process later. |
exception = new UnauthorizedAccessException(win32Exception.Message, win32Exception); | ||
} | ||
else if (errorCode == 32) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
File.GetAttributes
doesn't spend this extra effort in case the errorCode is 32 (ERROR_SHARING_VIOLATION). So, this is potentially a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same happens to WinSafeGetFileAttributes
which is removed by #6909.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see the PR description (Fix is ready in CoreFX, we get it in 2.1.1.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see the PR description - we'll get the fix in .Net Core 2.1.1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so this will only affect c:\pagefile.sys
? If so, then it's fine to leave it as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Three files - pagefile.sys, hiberfil.sys, swapfile.sys. In the CoreFX PR a comment says that they can not emulate the behavior and so use the real file c:\pagefile.sys in test. I think users are unlikely to find something like this
// Use 'File.GetAttributes()' because we want to get access exceptions. | ||
// TODO: we should review the tricky logic | ||
// (historically we throw 'UnauthorizedAccessException' here) and migrate | ||
// to standard methods 'File.Exist()' and 'Directory.Exists()' where possible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why having this todo comment? File.GetAttributes
will also throw UnauthorizedAccessException
when the error is ERROR_ACCESS_DENIED
.
And can you please point me to the places where it turns to File.Exists()
and Directory.Exits()
when NativeItemExists
fails?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems my comment is not clear.
I meant that we should migrate from the ItemExists method to File.Exists() and Directory.Exits() which don't throw UnauthorizedAccessException.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree. I also pointed that out in my comments. Open an issue to track that migration instead of this comment. This comment is not useful in the context of this method, which is designed to throw on error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done #7031
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO comment was removed.
return Platform.NonWindowsIsFile(path); | ||
#else | ||
|
||
#if !UNIX | ||
if (IsReservedDeviceName(path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to enclose this in #if !Unix
. The method body already separate the Unix/Windows code path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems method in line 898 (internal static bool ItemExists(string path)) is used on Unix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at the method IsReservedDeviceName
, it's body already takes care of the #if/def
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:-) I looked the other way.
Fixed.
@@ -4111,7 +4111,7 @@ private ExternalScriptInfo FindLocalizedModuleManifest(string path) | |||
|
|||
String filePath = stringBuilder.ToString(); | |||
|
|||
if (Utils.NativeFileExists(filePath)) | |||
if (File.Exists(filePath)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use Utils.FileExists
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Utils.FileExists behavior is the same (no exception is thrown). So it is better to use the standard method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to make all the Utils.NativeFileExists -> File/Directory.Exists
changes in a separate PR. So for now, just do the simple replacement of Utils.NativeFileExists -> Utils.FileExists
.
@@ -233,7 +233,7 @@ internal static IEnumerable<string> GetDefaultAvailableModuleFiles(string topDir | |||
foreach (string ext in ModuleIntrinsics.PSModuleExtensions) | |||
{ | |||
string moduleFile = Path.Combine(directoryToCheck, proposedModuleName) + ext; | |||
if (!Utils.NativeFileExists(moduleFile)) | |||
if (!File.Exists(moduleFile)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
Actually, I see Utils.NativeFileExists
and File.Exists
mixed in this file (possibly in that files too). Do we really care about the exception on failure in those cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Utils.FileExists behavior is the same (no exception is thrown). So it is better to use the standard method.
Yes, we should review all these cases but there are dozens of them to do in this PR or in a new?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. let's do it in a separate PR. For this PR, just do the simple rename replacement: Utils.NativeFileExists -> Utils.FileExists
.
} | ||
|
||
if (accessException != null) | ||
catch (Exception accessException) | ||
{ | ||
ErrorRecord errorRecord = new ErrorRecord(accessException, "RemoveFileSystemItemUnAuthorizedAccess", ErrorCategory.PermissionDenied, directory); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks to me it's expecting an UnauthorizedAccessException
, shall we narrow the catch to only catch this type of exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I only preserve the logic.
Really we should clean up the code too because .Net Core support reparse points and recursion https://github.com/dotnet/corefx/blob/f25eb288a449010574a6e95fe298f3ad880ada1e/src/System.IO.FileSystem/src/System/IO/FileSystem.Windows.cs#L359
Seems we should clean up all code in FileSystemProvider - perhaps we don't need FileSystemProviderV2 :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@daxian-dbw I opened #7017 to clean up the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine leaving this catch as is.
{ | ||
throw accessException; | ||
} | ||
result = Utils.ItemExists(path, out bool unused); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"discard" variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
string sysRoot = System.Environment.GetEnvironmentVariable("SystemRoot"); | ||
string urlmonPath = Path.Combine(sysRoot, @"System32\urlmon.dll"); | ||
if (Utils.NativeFileExists(urlmonPath)) | ||
if (Utils.FileExists(urlmonPath)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least for these two uses, they do not expect exception to be thrown. So change them to File.Exists
should be fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe revert these two changes too, since we agreed to use a separate PR for the migration changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted.
// This is done through P/Invoke since we pay 13% performance degradation | ||
// through the CAS checks required by File.Exists and Directory.Exists | ||
internal static bool NativeDirectoryExists(string path) | ||
internal static bool DirectoryExists(string path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should change the name of this one and FileExists
to something that indicate it might throw exception, so it's easy to tell whether you should use them instead of File.Exists
and Directory.Exists
when writing code.
I suggest rename them to CheckFileExistsAndThrowOnError
and CheckDirectoryExistsAndThrowOnError
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll rename if we will not migrate to File.Exists/Directory.Exists in the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, let's use a separate PR to do the migration changes. Go ahead rename this method.
// This is done through P/Invoke since File.Exists and Directory.Exists pay 13% performance degradation | ||
// through the CAS checks, and are terribly slow for network paths. | ||
internal static bool NativeItemExists(string path, out bool isDirectory, out Exception exception) | ||
internal static bool ItemExists(string path, out bool isDirectory) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, maybe changing this to CheckItemExistsAndThrowOnError
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll rename if we will not migrate to File.Exists/Directory.Exists in the PR.
@daxian-dbw I am trying to keep the PR as small as possible. There are dozens of places where we should consider replacing old code (ItemExists) with File.Exists (Directory.Exists). Do you want I do it in this PR? |
@iSazonov No, you don't need to do it in this PR. Open an issue to track the analysis and migration work (Utils.FileExists/DirectoryExists => File.Exists/Directory.Exists). |
Tracking Issue #7031 |
@daxian-dbw File.Exists() and Directory.Exists() return false for device names so we can remove IsReservedDeviceName call from ItemExists() method at all. What do you think? |
@iSazonov |
The changes LGTM. |
No, I'll remove the methods in follow PR.
Never mind - I hope to remove ItemExists() altogether in follow PR. |
@daxian-dbw Should I fix something else? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov If you plan to remove those methods in your next PR, then it's fine to leave their names unchanged.
@daxian-dbw My thoughts was that if we have to re-publish psl-native after every change |
PR Summary
Related #6874. After the PR we can remove the files - isfile.cpp, isfile.h, isdirectory.cpp, isdirectory.h - from psl.
PR Checklist
.h
,.cpp
,.cs
,.ps1
and.psm1
files have the correct copyright headerWIP:
to the beginning of the title and remove the prefix when the PR is ready.[feature]
if the change is significant or affects feature tests