Skip to content

Fix potential crash in repository cache fallback #3221

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

Merged
merged 1 commit into from
Jan 25, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Sources/SourceControl/RepositoryManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,8 @@ public class RepositoryManager {
} catch {
// Fetch without populating the cache in the case of an error.
print("Skipping cache due to an error: \(error)")
// It is possible that we already created the directory before failing, so clear leftover data if present.
try fileSystem.removeFileTree(repositoryPath)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm pretty sure that this can't spuriously fail because repositoryPath is missing, but could it fail because its parent path is missing or other reason? i.e. would it be better to deal with exceptions from here or let them raise?

Copy link
Contributor

Choose a reason for hiding this comment

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

testing if it exists before deleting sounds reasonable, then raising if we can't delete otherwise we will have downstream issues?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's probably not a problem in this particular case, but in general, checking for existence first can be prone to race conditions. So a pattern that usually works well for cases like this is to just do the file system operation but to silently ignore benign problems. In this case, an ENOENT error is fine but anything else should be reported. That might be what the underlying API already does, so my question was just to confirm that semantic. I'll check the code.

Copy link
Contributor Author

@neonichu neonichu Jan 25, 2021

Choose a reason for hiding this comment

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

Today it does this:

if self.exists(path, followSymlink: false) {
    try FileManager.default.removeItem(atPath: path.pathString)
 }

So the race potential is there.

Copy link
Contributor

@abertelrud abertelrud Jan 25, 2021

Choose a reason for hiding this comment

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

That's possibly a follow-on fix down in TSC, but it looks as if the code in this particular PR is correct (up in SwiftPM). I seems like correct behavior that FileSystem.removeFileTree(repositoryPath) doesn't throw an error if the file isn't already there (much like mkdir doesn't complain if the directory is there). So the code in this PR is right but TSC could use a fix, it looks like. So I have no concerns about this PR. Thanks!

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 think what would make the most sense is to leave this as-is but change removeFileTree in TSC to not throw on ENOENT as suggested, but for the 5.4 cherry-pick we inline the ENOENT check to reduce the surface area of the change.

try self.provider.fetch(repository: handle.repository, to: repositoryPath)
fromCache = false
}
Expand Down