aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Block <ablock84@gmail.com>2024-04-02 11:31:17 +0200
committerAlexander Block <ablock84@gmail.com>2024-04-05 23:35:33 +0200
commit7bd40f548987b3f0ce544902b9c4fbdf8d310882 (patch)
treeaa205a898b67f9a6a65267faff42d65e2d234cb1
parent7a9304e5a194f193cf413507fa1682ea97cc5216 (diff)
downloadgo-git-7bd40f548987b3f0ce544902b9c4fbdf8d310882.tar.gz
plumbing: Properly detect EOF when reading index extensions
Before this, go-git was relying on the peeked header to not include a valid 4 char string header. While doing this, it did not differentiate between the errournously read final hash and an unknown extension. This made it impossible to properly skip unknown optional extensions while detecting EOF early enough.
-rw-r--r--plumbing/format/index/decoder.go70
-rw-r--r--storage/filesystem/index.go2
2 files changed, 37 insertions, 35 deletions
diff --git a/plumbing/format/index/decoder.go b/plumbing/format/index/decoder.go
index 6778cf7..6f32418 100644
--- a/plumbing/format/index/decoder.go
+++ b/plumbing/format/index/decoder.go
@@ -39,6 +39,7 @@ const (
// A Decoder reads and decodes index files from an input stream.
type Decoder struct {
+ buf *bufio.Reader
r io.Reader
hash hash.Hash
lastEntry *Entry
@@ -49,8 +50,10 @@ type Decoder struct {
// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
h := hash.New(hash.CryptoType)
+ buf := bufio.NewReader(r)
return &Decoder{
- r: io.TeeReader(r, h),
+ buf: buf,
+ r: io.TeeReader(buf, h),
hash: h,
extReader: bufio.NewReader(nil),
}
@@ -210,64 +213,64 @@ func (d *Decoder) readExtensions(idx *Index) error {
// count that they are not supported by jgit or libgit
var expected []byte
+ var peeked []byte
var err error
- var header [4]byte
+ // we should always be able to peek for 4 bytes (header) + 4 bytes (extlen) + final hash
+ // if this fails, we know that we're at the end of the index
+ peekLen := 4 + 4 + d.hash.Size()
+
for {
expected = d.hash.Sum(nil)
-
- var n int
- if n, err = io.ReadFull(d.r, header[:]); err != nil {
- if n == 0 {
- err = io.EOF
- }
-
+ peeked, err = d.buf.Peek(peekLen)
+ if len(peeked) < peekLen {
+ // there can't be an extension at this point, so let's bail out
+ err = nil
break
}
+ if err != nil {
+ return err
+ }
- err = d.readExtension(idx, header[:])
+ err = d.readExtension(idx)
if err != nil {
break
}
}
- if err != errUnknownExtension {
+ if err != nil && err != errUnknownExtension {
return err
}
- return d.readChecksum(expected, header)
+ return d.readChecksum(expected)
}
-func (d *Decoder) readExtension(idx *Index, header []byte) error {
- switch {
- case bytes.Equal(header, treeExtSignature):
- r, err := d.getExtensionReader()
- if err != nil {
- return err
- }
+func (d *Decoder) readExtension(idx *Index) error {
+ var header [4]byte
+ if _, err := io.ReadFull(d.r, header[:]); err != nil {
+ return err
+ }
+
+ r, err := d.getExtensionReader()
+ if err != nil {
+ return err
+ }
+
+ switch {
+ case bytes.Equal(header[:], treeExtSignature):
idx.Cache = &Tree{}
d := &treeExtensionDecoder{r}
if err := d.Decode(idx.Cache); err != nil {
return err
}
- case bytes.Equal(header, resolveUndoExtSignature):
- r, err := d.getExtensionReader()
- if err != nil {
- return err
- }
-
+ case bytes.Equal(header[:], resolveUndoExtSignature):
idx.ResolveUndo = &ResolveUndo{}
d := &resolveUndoDecoder{r}
if err := d.Decode(idx.ResolveUndo); err != nil {
return err
}
- case bytes.Equal(header, endOfIndexEntryExtSignature):
- r, err := d.getExtensionReader()
- if err != nil {
- return err
- }
-
+ case bytes.Equal(header[:], endOfIndexEntryExtSignature):
idx.EndOfIndexEntry = &EndOfIndexEntry{}
d := &endOfIndexEntryDecoder{r}
if err := d.Decode(idx.EndOfIndexEntry); err != nil {
@@ -290,11 +293,10 @@ func (d *Decoder) getExtensionReader() (*bufio.Reader, error) {
return d.extReader, nil
}
-func (d *Decoder) readChecksum(expected []byte, alreadyRead [4]byte) error {
+func (d *Decoder) readChecksum(expected []byte) error {
var h plumbing.Hash
- copy(h[:4], alreadyRead[:])
- if _, err := io.ReadFull(d.r, h[4:]); err != nil {
+ if _, err := io.ReadFull(d.r, h[:]); err != nil {
return err
}
diff --git a/storage/filesystem/index.go b/storage/filesystem/index.go
index a19176f..a86ef3e 100644
--- a/storage/filesystem/index.go
+++ b/storage/filesystem/index.go
@@ -48,7 +48,7 @@ func (s *IndexStorage) Index() (i *index.Index, err error) {
defer ioutil.CheckClose(f, &err)
- d := index.NewDecoder(bufio.NewReader(f))
+ d := index.NewDecoder(f)
err = d.Decode(idx)
return idx, err
}