Hardlink notifications on Mac & Windows and rename notifications on Mac#203
Conversation
sanoursa
left a comment
There was a problem hiding this comment.
Just a quick first pass to make sure I understand the overall logic. I still need to spend a little more time with it, and test it out as well.
|
|
||
| public override void CreateHardLink(string targetPath, string newLinkPath) | ||
| { | ||
| Assert.Fail($"{nameof(PowerShellRunner)} does not support {nameof(this.CreateHardLink)}"); |
There was a problem hiding this comment.
Nit: move this to the base class?
There was a problem hiding this comment.
Sure, I'll move a default SupportsHardlinkCreation to the base as well
| string relativePath, | ||
| string destinationPath) | ||
| { | ||
| this.OnHardLinkCreated(relativePath, destinationPath); |
There was a problem hiding this comment.
Are these parameters ordered properly? The first parameter is ambiguous: relativePath could map to either relativeTargetPath or relativeNewLinkPath. But the second parameter sure looks like it maps better to relativeTargetPath, which makes me think these are swapped?
There was a problem hiding this comment.
These are ordered correctly.
I agree the naming could be better (and I could change these names), but as a start I went with the approach of matching the parameter names declared in the ProjFS Managed API:
/// <param name="relativePath">The path, relative to the virtualization root, of the file for
/// which the hard link was created.
/// <para>This parameter will be "" to indicate that the hard link name was created inside
/// the virtualization instance, i.e. a new hard link was created inside the virtualization
/// instance to a file that exists outside the virtualization instance.</para>
/// </param>
/// <param name="destinationPath">The path, relative to the virtualization root, of the new hard
/// link name.
/// <para>This parameter will be "" to indicate that the hard link name was created
/// outside the virtualization instance, i.e. a new hard link was created outside the
/// virtualization instance for a file that exists inside the virtualization instance.</para>
/// </param>
Let me know if you think it's better to match the API names or use our own names.
| } | ||
| } | ||
|
|
||
| protected void OnHardLinkCreated(string relativeTargetPath, string relativeNewLinkPath) |
There was a problem hiding this comment.
Why do we even need the relativeTargetPath parameter? It doesn't seem to be used at all.
There was a problem hiding this comment.
And why would it always be relative? Couldn't you create a hardlink inside your repo to some path outside of it? It seems like the target would need to be treated as an absolute path.
There was a problem hiding this comment.
Why do we even need the relativeTargetPath parameter? It doesn't seem to be used at all.
It's not strictly needed, but we do log it if there's an exception:
metadata.Add(nameof(relativeTargetPath), relativeTargetPath);
My thought was that if something goes wrong it's helpful to have as much information as we can.
And why would it always be relative? Couldn't you create a hardlink inside your repo to some path outside of it? It seems like the target would need to be treated as an absolute path.
See related comment as well (for detailed descriptions of these paths), if either of the paths are outside of the repo they will be empty strings.
| Console.WriteLine($"OnNewFileCreated (isDirectory: {isDirectory}): {relativePath}"); | ||
| } | ||
|
|
||
| private void OnFileRenamed(string relativeDestinationPath, bool isDirectory) |
There was a problem hiding this comment.
It seems really odd to get a rename event with only a single path in it. I understand the reasons why, but it's confusing from an API perspective. Should we just treat this as a new file event instead, and just not have a rename event?
There was a problem hiding this comment.
For historical purposes, it should be noted that we could deliver both paths on Mac, but because VFSForGit does not strictly need the source path on Mac we opted to keep the kext simpler and only deliver the destination path for now.
but it's confusing from an API perspective. Should we just treat this as a new file event instead, and just not have a rename event?
I've found it helpful when debugging\developing to keep these as separate events (and Keeping them as separate events also makes us more consistent with ProjFS on Windows).
I lean toward keeping them separate and having VFSForGit decide to handle this notification in the same way as a new file notification.
There was a problem hiding this comment.
I've added some additional comments in the notification callback
There was a problem hiding this comment.
I've added some additional comments in the notification callback
Actually, comments were added in VFSForGit, not in the MirrorProvider
| } | ||
|
|
||
| private void OnFileRenamed( | ||
| string relativePath, |
There was a problem hiding this comment.
See other comment as well. Currently these names match the names used in the ProjFS API, let me know if you think it's better to keep them consistent or change them.
| } | ||
|
|
||
| private void OnHardlinkCreated( | ||
| string relativePath, |
There was a problem hiding this comment.
Same question as elsewhere - there's no guarantee this is relative, right?
There was a problem hiding this comment.
Er, for one of these two parameters. I actually can't tell which one is the path of the link and which one is the path of the target :-)
There was a problem hiding this comment.
Same question as elsewhere - there's no guarantee this is relative, right?
As discussed elsewhere, any paths outside of the root will be an empty string (by design)
I actually can't tell which one is the path of the link and which one is the path of the target :-)
Same comment here too, do you think we should use our own names or stay consistent with ProjFS?
| static uint32_t ReadVNodeFileFlags(vnode_t vn, vfs_context_t context); | ||
| static inline bool FileFlagsBitIsSet(uint32_t fileFlags, uint32_t bit); | ||
| static inline bool FileIsFlaggedAsInRoot(vnode_t vnode, vfs_context_t context); | ||
| static inline bool ActionBitIsSet(kauth_action_t action, kauth_action_t mask); |
There was a problem hiding this comment.
Just curious, why did you take the inlines back out?
There was a problem hiding this comment.
We can add them back.
I remembered while working on the code that the inline is only required on the definition and not the declaration (see: https://isocpp.org/wiki/faq/inline-functions#inline-nonmember-fns).
But it doesn't hurt anything to leave them (and makes it clearer that they are expected to be inline) so I can add them back in.
|
|
||
| vtype vnodeType = vnode_vtype(currentVnode); | ||
| if (ShouldIgnoreVnodeType(vnodeType, currentVnode)) | ||
| // TODO(Mac): Improve error handling |
There was a problem hiding this comment.
And more importantly, we just lost a notification here. We will need some pattern for dealing with that.
There was a problem hiding this comment.
Good point, I will add some more details to this comment
| int kauthResult; | ||
| int kauthError; | ||
| if (!TrySendRequestAndWaitForResponse( | ||
| root, |
There was a problem hiding this comment.
For consistency with your other code, indent one more level here
| [TestCase, Order(2)] | ||
| public void CreateHardLinkTest() | ||
| { | ||
| if (!this.fileSystem.SupportsHardlinkCreation) |
There was a problem hiding this comment.
This test is not getting any coverage on Mac as it's not using BashRunner
There was a problem hiding this comment.
Test fixture updated to use all runners (whatever used to cause failures with BashRunner is no longer an issue)
| [Category(Categories.Mac.M2)] | ||
| public void AddAndStageHardLinksTest() | ||
| { | ||
| if (!this.FileSystem.SupportsHardlinkCreation) |
There was a problem hiding this comment.
This is not adding any coverage is AddStageTests are only run with SystemIORunner
There was a problem hiding this comment.
Updated CreateHardLink to always use BashRunner to create the hard link.
sanoursa
left a comment
There was a problem hiding this comment.
The updated code looks great, and I confirmed that all the functional tests pass on my machine. I didn't get a chance to do any manual testing yet, but no need to block on that.
| return; | ||
| } | ||
|
|
||
| this.CreateHardLink("Readme.md", "ReadmeLink.md"); |
There was a problem hiding this comment.
Nit: in the IPlatformFileSystem interface up above, I saw that the link name was first and the target name was second. I think it would help us keep these straight if we keep a consistent ordering through all of our own code.
| // creation) so use a BashRunner instead. | ||
| this.FileSystem.ShouldBeOfType<SystemIORunner>(); | ||
| this.FileSystem.SupportsHardlinkCreation.ShouldBeFalse( | ||
| "FileSystem *does* support hard link creation, CreateHardLink no longer needs to create a BashRunner"); |
There was a problem hiding this comment.
This message is confusing. You're saying that if in the future it ends up supporting hardlinks, we can remove this code?
There was a problem hiding this comment.
Yep that's right, let me try re-working this message.
Part of the work for #152 and fixes #200.
Summary of changes
Future work
Unlike VFSForGit on Windows, on Mac users can rename\move folders have not been expanded. To ensure that all placeholders are on disk prior to the rename the folder must be expanded in the PreDelete notification (#202)
Functional test run (on Mac) with these changes: