-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Make Get-ChildItem follow symlinks on demand, with checks for link loops #4020
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
Conversation
|
@iSazonov, @daxian-dbw can someone look at the AppVeyor failure? Looking at the details, It appears to be failing very early in the run |
|
@jeffbi I believe it is temporary error - please restart CI by reward your commit. |
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 should use autoproperty if we can.
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.
Modified the -FollowSymlink parameter to set -Recurse, and the -Depth parameter does. Thus we cannot use auto-property here.
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.
Closed.
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.
Can we use a Tuple<dev, inode> as key? If so it exclude "dictionary in dictionary".
Sample and c# 7.0 pattern https://stackoverflow.com/questions/2877660/composite-key-dictionary
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.
Part of that discussion (https://stackoverflow.com/questions/2877660/composite-key-dictionary#comment14110652_2877820) suggests that the nested dictionary approach may be faster.
As for the C#7 pattern, I thought I read somewhere in another code review that C#7 code should be avoided. Is that not the case?
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.
C#7 pattern
We have no strict documentation. Different maintainers may have different requirements. My understanding: New patterns are welcome for the new code. Existing patterns (not relevant to the PR) should not be changed without maintainer's requiring.
the nested dictionary approach may be faster.
There, the main answer has recently been updated and there is a reference to gist with the tests. The conclusion is now Tuple keys is fast.
We both think that performance is important here. So maybe it makes sense to spend time testing cases.
Another thought is, in most cases, the user will have one volume. We could use one dictionary until we find a reference to another volume. Although it will complicate the code and it's not clear how much we win. Ooops, for other volume we can make separate recursive Dir() call - in the case we have to switch Dictionary (tracker) only on "top" level and only on Symlink when we can meet new volume. In other words, we will have two dictionaries but they will not be nested and the number of comparisons will be less - we'll be faster.
And maybe rename Dir() in something like DirectoryVisitor() ?
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 may be that on Windows the user will have only one volume, but that is much less the case on Unix. For example, on my Linux box the root (/) directory has device ID 2049, the /dev directory has ID 6, and the /proc directory has ID 4. Symlinks can cross device/volume boundaries.
I've changed the code to use the C#7 ValueTuple mechanism.
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 this on top level - see lines 1610-1616. I believe we should remove the two lines.
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.
This function is called recursively, so I think we need to leave these lines here.
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.
tracker = new InodeTracker(); - It's not expensive. We can always create this on top level and not check many times during recursion.
We can tracker.Visit(directory.FullName); before call the Dir.
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.
In the Dir function, the tracker object also acts as a flag that the user gave the -FollowSymlink parameter.
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 tried to refactor code and had no benefits.
Closed.
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 we check "recurse" here? Have we Recurse and FollowSymlink in only the same ParameterSet?
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.
There are two parameter sets for Get-ChildItem: Items and LiteralItems. The -Recurse and -FollowSymlink parameters are in both parameter sets.
The Depth parameter, which is also in both, implies -Recurse by setting the Recurse property. Should -FollowSymlink follow that pattern? That would remove the check of recurse here.
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 can not define explicitly mandatory sub-parameters 😕 That would be a useful feature.
So it is more safe to leave "recurse" here.
If -Depth implies -Recurse I believe we can do the same with -FollowSymlink .
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.
Modified the -FollowSymlink parameter to set -Recurse, and the -Depth parameter does.
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.
Context.MyInvocation.BoundParameters.ContainsKey("FollowSymlink"))
Can we use FollowSymlink property?
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.
FollowSymlink is a property of GetChildItemCommand, not of the FileSystemProvider.
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.
Thanks for clarify!
Closed.
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 can move "tracker != null" and exclude one "if":
if (tracker != null && tracker.Visited(recursiveDirectory.FullName)) 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 seems we can exclude enterDir:
if (tracker != null)
{
if (tracker.Visited(recursiveDirectory.FullName))
{
WriteWarning(StringUtil.Format(FileSystemProviderStrings.AlreadyListedDirectory,
recursiveDirectory.FullName));
continue;
}
}
else
{
if (InternalSymbolicLinkLinkCodeMethods.IsReparsePoint(recursiveDirectory))
{
continue;
}
}
bool hidden = false;
if (!Force)
{
hidden = (recursiveDirectory.Attributes & FileAttributes.Hidden) != 0;
}
// if "Hidden" is explicitly specified anywhere in the attribute filter, then override
// default hidden attribute filter.
if (Force || !hidden || isFilterHiddenSpecified || isSwitchFilterHiddenSpecified)
{
Dir(recursiveDirectory, recurse, depth - 1, tracker, nameOnly, returnContainers);
}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.
Refactored code to eliminate enterDir.
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.
Closed.
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 re-format the comment. Maybe put a) and b) on new lines. And "the directory the symlink" confuse me.
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.
Modified the comment.
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.
Closed.
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.
"{0}" can bee very long so maybe we re-format the message?
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.
Re-formatted the message to put the path at the end.
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.
Closed.
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.
Duplicated "@brief" ?
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.
This one describes the function. The other describes the file.
I used the existing getlinkcount.cpp as the pattern for this.
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.
Closed.
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.
Can we use the pattern?
enterDir = tracker != null;Or we should use explicit "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.
Closed.
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.
Is it supported on Windows 7?
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 supported as early as XP.
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.
Closed.
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.
Closed.
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.
Context.MyInvocation.BoundParameters.ContainsKey("FollowSymlink"))
Can we use FollowSymlink property?
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.
Now we don't use dev and inode and we could out Tuple.
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 do still use dev and inode. That's how we uniquely identify a file system object.
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 see the GetInodeData is only called in Visit and Visited 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.
Yes. Did you want to move the implementation up into the InodeTracker class?
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 meant simple change like internal static bool GetInodeData(string path, out (dev, inode) var)
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.
Closed.
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.
Can we exclude repetition "listing ... listed"?
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.
Changed "already-listed" to "already-visited"
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.
Closed.
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.
In line 170
this.Recurse = true; // Bug 2391925 - Get-ChildItem -Depth should auto-set -RecurseShould we use the same pattern?
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 looks like that was in response to a specific bug. In our case it's just part of a new feature.
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 afraid we could catch the same bug here. Maybe maintainers clarify.
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 question for maintainers: should we follow the file patterns and move the private variable to a line below 342?
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 use the same pattern in Visited and Visit methods - insert a new line here or remove a line after "var" in Visit.
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.
Thanks for catching that. New line inserted.
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.
Closed.
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.
Do it makes sense to break it down into two lines?
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.
Made the calculation more concise.
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.
Closed.
|
@daxian-dbw Could you please assign a reviewer? |
|
@jeffbi Great work! Thanks! |
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 don't use public modifier for members of an internal class. Use internal/private as appropriate.
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.
This method name should be IsPathVisited. It's good for readability.
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.
I think -FollowSymlink should be a dynamic parameter provided by file system provider so that when using Get-ChildItem with another provider, this parameter won't show up.
-FollowSymlink is very much like the -CodeSigningCert parameter of Get-ChildItem -- -CodeSigningCert only applicable to the certificate provider and -FollowSymlink only applicable to FileSystemProvider. It's unlike the -Depth parameter which could be commonly meaningful to many underlying providers.
PS:17> Get-ChildItem Cert:\ -CodeSigningCert -Recurse
PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\REQUEST
Thumbprint Subject
---------- -------
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX CN=BLAHBLAH
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX CN=LAHBLAHBIt's doable to make -FollowSymlink depends on the appearance of -Recurse in the dynamic parameter, but I suggest for this PR let's make things simpler by not having this design. If it's a highly demanded feature, we can enable it in a separate PR.
Please take a look at the following places to see how dynamic parameters are retrieved from the underlying provider:
https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.Commands.Management/commands/management/GetChildrenCommand.cs#L372-L387
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.
So then -FollowSymlink should not imply -Recurse? It has meaning only if -Recurse is also explicitly given?
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.
powercode initiated #4102 for disscusion of parameter sets selection. So it is design case whether or not imply "mandatory" parameter by subparameter.
And what is if a path in -Path is Symlink?
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.
Event without -FollowSymlink, the cmdlet will follow symlinks specified in -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.
Should we follow w/o FollowSymlink in the case? If no we can use FollowSymlink w/o Recurce. If yes we should rename FollowSymlink in FollowSymlinkInRecursion.
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 it will turn out that making -FollowSymlink implicitly imply -Recurse is better, but for now here are my 2 cents:
- It's always better to force the user to be explicit about their intent, so
dir -Recurse -FollowSymlinkis more readable thandir -FollowSymlinkin my opinion. - For simplicity reason, this PR can just add the
-FollowSymlinkparameter without worrying about its dependency to-Recurse. In my understanding (correct me if I'm wrong) the related code won't execute when it's not recursively iterating file system items. Then we can get feedback from users and base on that to decide if we should make-FollowSymlinkimplicitly imply-Recurse.
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.
Changed to dynamic parameter. -FollowSymlink no longer implies -Recurse.
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.
if (recurse && Context.MyInvocation.BoundParameters.ContainsKey("FollowSymlink"))
This is fragile as you don't check the value. Think about dir -follow:$false. We should go with the dynamic parameter approach for -FollowSymlink
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 Skip already-visited directory {0} might be better.
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 this be "Skip"? Or perhaps "Skipping" or "Skipped"?
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.
I see that you are following a pattern like IsSameFileSystemItem or IsHardLink. Ideally, code here should be refactored to consolidate Unix/Windows implementations in one place and eliminate wrapper functions like IsSameFileSystemItem and IsHardLink. Instead they should be
internal static bool IsSameFileSystemItem(string pathOne, string pathTwo)
{
#if UNIX
// Unix implementation goes here
#else
// Windows implementation goes here
#endif
}I'm not asking you to do this change for this PR, just want to call out that it should be this way moving forward. I opened #4086 to track the refactoring task.
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.
Correspondingly, maybe this name can be changed to VisitPath.
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.
And, does this method really need to return anything? If GetInodeData failed, then we just don't skip anything.
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 both.
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.
Now we return nothing. Please remove the line.
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.
Closed.
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.
Wrong pattern.
We can use tracker?.VisitPath(directory.FullName); or if (...) { ... }.
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.
Closed.
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 can remove one if.
if (fspDynamicParam?.FollowSymlink) 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.
Sorry, I again under 'tracker == null' doulble check pressure.
It seem we can remove one if remove lines 1675-1678, add its before first Dir call and here add:
else
{
tracker.VisitPath(directory.FullName);
}Next, we can exclude VisitPath at all if make "visit" in IsPathVisited (we should rename it maybe as TryPathVisit).
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 don't agree with this. If we mark the directory has having been visited here, then if the test at line 1883 evaluates to false the Dir method will not be called and we will not visit the directory even though we have already claimed to have done so.
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.
Do you mean "hidden" directories? If so we can safely mark "hidden" directories as "visited" - in both cases they is really skipped.
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.
A directory might be hidden. A symbolic link (or several such) might not be.
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 are right. We can move the hidden check above line 1861 - it is less expensive then double call 'GetInodeData' in VisitPath and IsPathVisited.
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.
Now CoreFX has a new method Dictionary.TryAdd - Could we use them?
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.
Used new method
|
LGTM. @daxian-dbw Could you please continue the code review? |
daxian-dbw
left a comment
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.
Thanks for switching to the dynamic parameter approach! See some more comments below.
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.
Missing </summary>
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.
Why using a Dictionary? It seems HashSet would be good.
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 use new Dictionary.TryAdd() but HashSet.Add() works the same - so we can use HashSet.
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.
HashSet is much better. 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.
A new parameter should usually be added to the end of the parameter list.
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.
If recurse == false, then we should avoid creating the tracker.
if (recurse)
{
GetChildDynamicParameters fspDynamicParam = DynamicParameters as GetChildDynamicParameters;
if (fspDynamicParam != null && fspDynamicParam.FollowSymlink)
{
tracker = new InodeTracker(directory.FullName);
}
}
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 thought I had a check for recurse at one time. It must have gotten lost in the shuffle. 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.
Here we can use else if (...) so that one nested block can be avoided.
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.
This method goes against the convention of Try<Verb> pattern. Take TryAdd as an instance, if added, then return true; if not added, then return false. Following that conversion, TryVisitPath should be: if visited, then return true; if not visited, then return false. So TryVisitPath makes it confusing understanding the code.
How about making it IsPathVisited(bool visitIfNotAlready = false)?
internal bool IsPathVisited(bool visitIfNotAlready = false)
{
bool isPathVisited = false;
if (InternalSymbolicLinkLinkCodeMethods.GetInodeData(path, out (UInt64, UInt64) inodeData))
{
if (visitIfNotAlready)
{
isPathVisited = !_visitations.Add(inodeData)
}
else
{
isPathVisited = _visitations.Contains(inodeData)
}
}
return isPathVisited;
}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.
With HashSet it will looks just better.
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.
This class seems to be a helper that will only be used in FileSystemProvider code, so can we make it a private nested class within FileSystemProvider? (just like private static class NativeMethods in FileSystemProvider)
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.
Minor comment: maybe use the following pattern to avoid var inodeData = (0UL, 0UL);?
if (InternalSymbolicLinkLinkCodeMethods.GetInodeData(path, out (UInt64, UInt64) inodeData))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 good.
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.
…ops. (#3951) Add -FollowSymlink switch parameter to Get-ChildItem. Add a mechanism for tracking visited directories. Add native code to get dev/inode information on Unix. Add warning when refusing to enter an already-visited directory.
o Switched from nested dictionary to single dictionary keyed by
ValueTuple (device, inode).
o Modified warning message to put path at end.
o -FollowSymlink switch now implies -Recurse
o Other code and comment refactorizings.
* Make InodeTracker a private nested class
* Change from Dictionary to HashSet in InodeTracker
* Change InodeTracker's TryVisitPath to IsPathVisited with a parameter for marking a path as visited if not already
* Add check for -Recurse before creating InodeTracker object
* Change order of parameters in Dir method
* Remove unneeded nested block level
* Add missing </summary> tag
* Add -Pending on some tests to get past Pester errors
(see issue #4145)
| /// </param> | ||
| /// <param name="visitIfNotAlready"> | ||
| /// Flag to indicate whether the path should be marked as visited if | ||
| /// it has not already been visited. |
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 we add the parameter if we don't use them?
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.
Normally I would say no. In this case the existence of the parameter and its name give a clue about the side effect of marking the path as visited.
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 give this some more thinking, and believes that the TryVisitPath signature is more natural in this case. But we need to follow the Try<> pattern.
Again, take HashSet.Add(object) as an example:
- If the object is not in the set, add it and return true.
- If the object is already in the set, return false which means it was not added in this call because it's already there.
Similarly, InodeTracker.TryVisitPath(path) should be
- If the path is not visited yet, visit it and return true.
- If the path is already visited, return false which means it was not visited in this call because it's already visited.
The code would be like:
internal bool TryVisitPath(string path)
{
bool returnValue = false;
if (InternalSymbolicLinkLinkCodeMethods.GetInodeData(path, out (UInt64, UInt64) inodeData))
{
returnValue = _visitations.Add(inodeData);
}
return returnValue;
}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 like the TryVisitPath more the IsVisitPath.
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.
Works for me. I'll make the 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.
Awesome. Thank you both!
daxian-dbw
left a comment
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.
2 more comments
| bool nameOnly, | ||
| ReturnContainers returnContainers) | ||
| ReturnContainers returnContainers, | ||
| InodeTracker tracker) // tracker will be non-null only if the user invoked the -FollowSymLinks switch parameter. |
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.
Minor comment: this comment is not accurate now -- tracker will be null when -FollowSymlinks is specified but -Recurse is not.
| /// </param> | ||
| /// <param name="visitIfNotAlready"> | ||
| /// Flag to indicate whether the path should be marked as visited if | ||
| /// it has not already been visited. |
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 give this some more thinking, and believes that the TryVisitPath signature is more natural in this case. But we need to follow the Try<> pattern.
Again, take HashSet.Add(object) as an example:
- If the object is not in the set, add it and return true.
- If the object is already in the set, return false which means it was not added in this call because it's already there.
Similarly, InodeTracker.TryVisitPath(path) should be
- If the path is not visited yet, visit it and return true.
- If the path is already visited, return false which means it was not visited in this call because it's already visited.
The code would be like:
internal bool TryVisitPath(string path)
{
bool returnValue = false;
if (InternalSymbolicLinkLinkCodeMethods.GetInodeData(path, out (UInt64, UInt64) inodeData))
{
returnValue = _visitations.Add(inodeData);
}
return returnValue;
}
daxian-dbw
left a comment
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.
|
@jeffbi Many thanks for the great work! |
|
@iSazonov and @daxian-dbw Thanks for the review. |
|
Very good job! P.S. I would probably have called the new parameter |
Fixes #3951