Skip to content
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

Merged
merged 20 commits into from
Jun 14, 2018
Merged

Conversation

iSazonov
Copy link
Collaborator

PR Summary

Related #6874. After the PR we can remove the files - isfile.cpp, isfile.h, isdirectory.cpp, isdirectory.h - from psl.

  • Use File.GetAttributes() instead of p/invokes. The method throws access exceptions (unlike File.Exists and Directory.Exists) which we use for better error messages.
  • Remove 'Native' from method names.
  • As stated in Cleanup FileSystem provider #6909 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).

PR Checklist

@iSazonov
Copy link
Collaborator Author

Reopen to restart CIs.

@iSazonov iSazonov closed this May 25, 2018
@iSazonov iSazonov reopened this May 25, 2018
@iSazonov
Copy link
Collaborator Author

@TravisEz13 Could you please help - I can not find why CIs failed?

@iSazonov
Copy link
Collaborator Author

Feature tests failed with Pester not found. 😕

catch
{
}
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be

return result;

Copy link
Collaborator Author

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);
Copy link
Member

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

Copy link
Member

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?

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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
Copy link
Member

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?

Copy link
Collaborator Author

@iSazonov iSazonov May 30, 2018

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.

@TravisEz13
Copy link
Member

The Linux failure does not seem related to your change
https://travis-ci.org/PowerShell/PowerShell/jobs/385527642#L903
image
I'm restarting the CI run

@iSazonov iSazonov force-pushed the psl-cleanup-isfile branch from 8e8ac45 to 67c5d33 Compare June 5, 2018 04:48
@iSazonov
Copy link
Collaborator Author

iSazonov commented Jun 5, 2018

@TravisEz13 Thanks! But CIs failed again. I rebased - perhaps it resolve..

iSazonov added 2 commits June 5, 2018 13:07
No need in throw here if file not found. (Previously we could throw in rare cases.)
@iSazonov
Copy link
Collaborator Author

iSazonov commented Jun 5, 2018

@SteveL-MSFT @TravisEz13 Please review last two commits. I found my mistake. I added new comment and TODO to continue the cleanup process later.

@daxian-dbw daxian-dbw self-assigned this Jun 6, 2018
exception = new UnauthorizedAccessException(win32Exception.Message, win32Exception);
}
else if (errorCode == 32)
{
Copy link
Member

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.

Copy link
Member

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.

Copy link
Collaborator Author

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.)

Copy link
Collaborator Author

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.

Copy link
Member

@daxian-dbw daxian-dbw Jun 8, 2018

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.

Copy link
Collaborator Author

@iSazonov iSazonov Jun 9, 2018

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.
Copy link
Member

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?

Copy link
Collaborator Author

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.

Copy link
Member

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done #7031

Copy link
Collaborator Author

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))
Copy link
Member

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.

Copy link
Collaborator Author

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.

Copy link
Member

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.

Copy link
Collaborator Author

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))
Copy link
Member

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 ?

Copy link
Collaborator Author

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.

Copy link
Member

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))
Copy link
Member

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?

Copy link
Collaborator Author

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?

Copy link
Member

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);
Copy link
Member

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?

Copy link
Collaborator Author

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 :-)

Copy link
Collaborator Author

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.

Copy link
Member

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"discard" variable.

Copy link
Collaborator Author

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))
Copy link
Member

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Member

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.

Copy link
Collaborator Author

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)
Copy link
Member

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.

Copy link
Collaborator Author

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.

Copy link
Member

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)
Copy link
Member

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.

Copy link
Collaborator Author

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.

@iSazonov
Copy link
Collaborator Author

iSazonov commented Jun 7, 2018

@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?

@daxian-dbw
Copy link
Member

@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).

@iSazonov
Copy link
Collaborator Author

iSazonov commented Jun 9, 2018

Tracking Issue #7031

@iSazonov
Copy link
Collaborator Author

iSazonov commented Jun 9, 2018

@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?

@daxian-dbw
Copy link
Member

@iSazonov ItemExists doesn't call FileExists or Directory.Exists. Do you mean File.GetAttributes returns -1 for reserved device names?

@daxian-dbw
Copy link
Member

The changes LGTM.
@iSazonov Are you going to rename FileExists, DirectoryExists and ItemExists to
CheckFileExistsAndThrowOnError, CheckDirectoryExistsAndThrowOnError and CheckItemExistsAndThrowOnError in this PR?

@iSazonov
Copy link
Collaborator Author

Are you going to rename FileExists, DirectoryExists and ItemExists to
CheckFileExistsAndThrowOnError, CheckDirectoryExistsAndThrowOnError and CheckItemExistsAndThrowOnError in this PR?

No, I'll remove the methods in follow PR.

ItemExists doesn't call FileExists or Directory.Exists. Do you mean File.GetAttributes returns -1 for reserved device names?

Never mind - I hope to remove ItemExists() altogether in follow PR.

@iSazonov
Copy link
Collaborator Author

@daxian-dbw Should I fix something else?

Copy link
Member

@daxian-dbw daxian-dbw left a 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
Copy link
Member

@iSazonov It's preferred that you remove the native code from psl-native along with your managed code changes in your following PRs. For the PRs you already finished, I listed the tasks to track removing the corresponding native code in #6874.

@daxian-dbw daxian-dbw merged commit 81244fb into PowerShell:master Jun 14, 2018
@iSazonov
Copy link
Collaborator Author

@daxian-dbw My thoughts was that if we have to re-publish psl-native after every change
it is better to finish first the cleanup in the repo and only then cleanup in psl-native repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants