Skip to content

Add repo root dir to retention list before projection updates#1160

Merged
chrisd8088 merged 1 commit intomicrosoft:masterfrom
github:skip-root-rmdir
May 16, 2019
Merged

Add repo root dir to retention list before projection updates#1160
chrisd8088 merged 1 commit intomicrosoft:masterfrom
github:skip-root-rmdir

Conversation

@chrisd8088
Copy link
Contributor

@chrisd8088 chrisd8088 commented May 15, 2019

The GitIndexProjection.UpdatePlaceholders() method, after processing the list of placeholder files, iterates over the list of placeholder folders and tries to remove any which are actually files, or not in the projection, or a possible tombstone.

The last placeholder folder in the ordered list appears to always correspond to the root of the repository; this placeholder has an empty path string, and IsPathProjected() returns false for both the isProjected and isFolder flags for this entry.

Because both flags are false, RemoveFolderPlaceholderIfEmpty() is called, which then results in a call to DeleteFile() in the lower-level ProjFS layer. This always fails, because the root folder is never empty (it contains, among other things, the .git folder), but since RemoveFolderPlaceholderIfEmpty() handles the FSResult.DirectoryNotEmpty error code, no exception is propagated.

We can avoid these unnecessary attempts to remove the repository's root folder on git checkout operations by adding the empty path to the folderPlaceholdersToKeep list.

This behaviour was noted while testing #1128, which implements the underlying DeleteFile() method on Linux, where the use of a true filesystem mount point for the root of the VFSForGit repository means that an attempt to rmdir(2) the repository's root directory does not return ENOTEMPTY but rather EBUSY (Device or resource busy). This is not silently handled by RemoveFolderPlaceholderIfEmpty() but instead logs an error.

/cc @jrbriggs, @kivikakk

@chrisd8088 chrisd8088 marked this pull request as ready for review May 15, 2019 03:51
@derrickstolee derrickstolee requested a review from kewillford May 15, 2019 11:45
Copy link
Contributor

@derrickstolee derrickstolee left a comment

Choose a reason for hiding this comment

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

I only have a minor style comment, but added @kewillford who is the expert with this projection stuff.

!isFolder ||
folderPlaceholder.IsPossibleTombstoneFolder)
folderPlaceholder.IsPossibleTombstoneFolder) &&
folderPlaceholder.Path != string.Empty)
Copy link
Member

Choose a reason for hiding this comment

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

How about we add the string.Empty to the folderPlaceholdersToKeep when we initialize it since we will always be keeping that folder?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about that too, but was concerned it might "match against everything" and short-circuit all the path comparisons. However, a little testing and a more careful read of the code shows this is not at all the case, so I rebased with your suggestion implemented in 46cca89. Thanks!

Copy link
Member

@wilbaker wilbaker left a comment

Choose a reason for hiding this comment

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

Waiting to hear thoughts on Kevin's suggestion to add the root to the "folders to keep" list

The GitIndexProjection.UpdatePlaceholders() method, after
processing the list of placeholder files, iterates over the
list of placeholder folders and tries to remove any which are
actually files, or not in the projection, or a possible tombstone.

The last placeholder folder in the ordered list appears to
always correspond to the root of the repository; this placeholder
has an empty path string, and IsPathProjected() returns false
for both the isProjected and isFolder flags for this entry.

Because both flags are false, RemoveFolderPlaceholderIfEmpty()
is called, which then results in a call to DeleteFile() in the
lower-level ProjFS layer.  This always fails, because the root
folder is never empty (it contains, among other things, the .git
folder), but since RemoveFolderPlaceholderIfEmpty() handles the
FSResult.DirectoryNotEmpty error code, no exception is propagated.

We can avoid these unnecessary attempts to remove the repository's
root folder on "git checkout" operations by adding the empty path
to the folderPlaceholdersToKeep list.

Co-authored-by: Kevin Willford <kewillf@microsoft.com>
@chrisd8088
Copy link
Contributor Author

@wilbaker -- reworked using @kewillford's much tidier suggestion!

Copy link
Member

@kewillford kewillford left a comment

Choose a reason for hiding this comment

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

Looks great thanks! Please update the title and description before merging.

@chrisd8088 chrisd8088 changed the title Skip attempts to remove repo root dir during projection updates Add repo root dir to retention list before projection updates May 16, 2019
@chrisd8088 chrisd8088 merged commit 83b23ee into microsoft:master May 16, 2019
@chrisd8088 chrisd8088 deleted the skip-root-rmdir branch May 16, 2019 06:08
@jrbriggs jrbriggs added this to the M153 milestone May 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants