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

Fix FilterFS when skipping lazy parent directories #183

Merged
merged 2 commits into from
Feb 21, 2024

Conversation

jedevc
Copy link
Collaborator

@jedevc jedevc commented Feb 19, 2024

Original issue found by @vito in dagger/dagger#6687.

Split into two commits, both with slightly different issues:

  • filter: allow SkipDir return with lazy parents. Ensures that walker functions that return filepath.SkipDir are always respected, even when evaluated as a lazy parent function (see commit for more details).
  • filter: ensure MapResult is followed for parent directories. Ensures that the FilterFS Map option for MapResultSkipDir is always respected, even when evaluated as a lazy parent function (see commit for more details).

These issues seem to have been present for a while (including prior to #167, which introduced FilterFS, where the issue was still present in fsutil.Walk).

When using include/exclude options in a file walk, we lazily store the
parents, and only later call the target function with them when we find
a file inside. This handles the case where `**/*.go` should only walk
directories that contain `.go` files, and not every directory in the
tree.

However, when storing lazy parents, we weren't appropriately handling
the `SkipDir` value that could be returned. We were making two incorrect
assumptions:

1. An error value from `fn()` was always an error case - this isn't
   true, since we have control error values like `SkipDir`. Because of
   this, we need to ensure that `calledFn` is *always* set to true when
   calling `fn` (even in an error case).
2. We can return `SkipDir` to skip this parent directory. This isn't
   *quite* right, since returning `SkipDir` would return it only for the
   child directory - we need to make sure it's called everytime for each
   child directory of this parent directory, by storing it as a boolean
   `skipFn` field.

With these changes, the new test passes, and we don't end up with
duplicate parent calls, as was happening before:

	Error:          Not equal:
			expected: []string{"foo"}
			actual  : []string{"foo", "foo"}

Signed-off-by: Justin Chadwell <[email protected]>
Similar to the previous commit, we weren't properly respecting MapResult
return values for parent directories. To resolve this, we just store
into `skipFn` just like previously, and return `SkipDir` instead of
`continue`ing on.

After these changes, the new test passes, and we don't end up including
entries that were in excluded directories, as was happening before:

	Error:      	Not equal:
			expected: "dir y\nfile y/b.txt\n"
			actual  : "file x/a.txt\ndir y\nfile y/b.txt\n"

Signed-off-by: Justin Chadwell <[email protected]>
Repository owner deleted a comment from codecov-commenter Feb 20, 2024
Copy link
Collaborator

@crazy-max crazy-max left a comment

Choose a reason for hiding this comment

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

LGTM

@tonistiigi tonistiigi self-requested a review February 20, 2024 17:30
})
assert.NoError(t, err)

assert.Equal(t, []string{"foo"}, found)
Copy link
Owner

Choose a reason for hiding this comment

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

Why would this return anything at all. "foo" does not match the IncludePatterns.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I assume because the Walk recurses into foo to see if anything matches the IncludePatterns...

Copy link
Collaborator

Choose a reason for hiding this comment

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

Walk finds a file that matches the include patterns, then starts calling Walk on parent directories, and the first Walk callback func returns SkipDir...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Exactly that, yup.

Copy link
Owner

Choose a reason for hiding this comment

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

I don't understand how this makes sense. IncludePatterns is an option to Walk. Callback should not be called on paths that don't match IncludePatterns.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If it's not included there then it isn't hooked up to Send/Recv - which means that we don't send the file stats for parent directories.

e.g. foo as this parent should have it's permissions/etc transferred through these methods. Potentially Send shouldn't use Walk at all, but that seems out of scope for this IMO, as it's a larger refactor and architectural change (not really convinced by it either).

continue
} else if result == MapResultSkipDir {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: don't need else if after a continue.

@tonistiigi tonistiigi merged commit b847aa4 into tonistiigi:master Feb 21, 2024
14 checks passed
@jedevc jedevc deleted the fix-parent-interactions branch March 7, 2024 14:41
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