Skip to content

Commit 3f1cfde

Browse files
authored
Merge pull request #659 from MichaelMure/dotgit-race
dotgit: fix a filesystem race in Refs/walkReferencesTree
2 parents 8557a36 + 660071d commit 3f1cfde

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

storage/filesystem/dotgit/dotgit.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,7 @@ func (d *DotGit) walkReferencesTree(refs *[]*plumbing.Reference, relPath []strin
943943
files, err := d.fs.ReadDir(d.fs.Join(relPath...))
944944
if err != nil {
945945
if os.IsNotExist(err) {
946+
// a race happened, and our directory is gone now
946947
return nil
947948
}
948949

@@ -960,6 +961,10 @@ func (d *DotGit) walkReferencesTree(refs *[]*plumbing.Reference, relPath []strin
960961
}
961962

962963
ref, err := d.readReferenceFile(".", strings.Join(newRelPath, "/"))
964+
if os.IsNotExist(err) {
965+
// a race happened, and our file is gone now
966+
continue
967+
}
963968
if err != nil {
964969
return err
965970
}

storage/filesystem/dotgit/dotgit_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,3 +864,71 @@ func (s *SuiteDotGit) TestIncBytes(c *C) {
864864
c.Assert(overflow, Equals, test.overflow)
865865
}
866866
}
867+
868+
// this filesystem wrapper returns os.ErrNotExist if the file matches
869+
// the provided paths list
870+
type notExistsFS struct {
871+
billy.Filesystem
872+
873+
paths []string
874+
}
875+
876+
func (f *notExistsFS) matches(path string) bool {
877+
p := filepath.ToSlash(path)
878+
for _, n := range f.paths {
879+
if p == n {
880+
return true
881+
}
882+
}
883+
return false
884+
}
885+
886+
func (f *notExistsFS) Open(filename string) (billy.File, error) {
887+
if f.matches(filename) {
888+
return nil, os.ErrNotExist
889+
}
890+
891+
return f.Filesystem.Open(filename)
892+
}
893+
894+
func (f *notExistsFS) ReadDir(path string) ([]os.FileInfo, error) {
895+
if f.matches(path) {
896+
return nil, os.ErrNotExist
897+
}
898+
899+
return f.Filesystem.ReadDir(path)
900+
}
901+
902+
func (s *SuiteDotGit) TestDeletedRefs(c *C) {
903+
fs, clean := s.TemporalFilesystem()
904+
defer clean()
905+
906+
dir := New(&notExistsFS{
907+
Filesystem: fs,
908+
paths: []string{
909+
"refs/heads/bar",
910+
"refs/heads/baz",
911+
},
912+
})
913+
914+
err := dir.SetRef(plumbing.NewReferenceFromStrings(
915+
"refs/heads/foo",
916+
"e8d3ffab552895c19b9fcf7aa264d277cde33881",
917+
), nil)
918+
c.Assert(err, IsNil)
919+
err = dir.SetRef(plumbing.NewReferenceFromStrings(
920+
"refs/heads/bar",
921+
"a8d3ffab552895c19b9fcf7aa264d277cde33881",
922+
), nil)
923+
c.Assert(err, IsNil)
924+
err = dir.SetRef(plumbing.NewReferenceFromStrings(
925+
"refs/heads/baz/baz",
926+
"a8d3ffab552895c19b9fcf7aa264d277cde33881",
927+
), nil)
928+
c.Assert(err, IsNil)
929+
930+
refs, err := dir.Refs()
931+
c.Assert(err, IsNil)
932+
c.Assert(refs, HasLen, 1)
933+
c.Assert(refs[0].Name(), Equals, plumbing.ReferenceName("refs/heads/foo"))
934+
}

0 commit comments

Comments
 (0)