diff options
author | Alexander Block <ablock84@gmail.com> | 2024-04-02 11:31:17 +0200 |
---|---|---|
committer | Alexander Block <ablock84@gmail.com> | 2024-04-05 23:35:33 +0200 |
commit | 7bd40f548987b3f0ce544902b9c4fbdf8d310882 (patch) | |
tree | aa205a898b67f9a6a65267faff42d65e2d234cb1 | |
parent | 7a9304e5a194f193cf413507fa1682ea97cc5216 (diff) | |
download | go-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.go | 70 | ||||
-rw-r--r-- | storage/filesystem/index.go | 2 |
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 } |