Compare commits

...

4 Commits

Author SHA1 Message Date
Rod Vagg
d46e7f2866 v0.4.1 2023-04-04 14:08:15 +10:00
gammazero
83a0e939a4 Add unit test for unexpected eof 2023-04-04 14:08:15 +10:00
Andrew Gillis
0981f8566c Update cid.go
Co-authored-by: Rod Vagg <rod@vagg.org>
2023-04-04 14:08:15 +10:00
gammazero
166a3a6880 CidFromReader should not wrap valid EOF return.
When reading from an io.Reader that has no data, the io.EOF error should not be wrapped in ErrInvalidCid. This is not an invalid CID, and is not the same as a partial read which is indicated by io.ErrUnexpectedEOF.

This fix is needed because existing code that uses CidFromReader may check for the end of an input stream by `if err == io.EOF` instead of the preferred `if errors.Is(err, io.EOF)`, and that code break at runtime after upgrading to go-cid v0.4.0.
2023-04-04 14:08:15 +10:00
3 changed files with 33 additions and 1 deletions

8
cid.go
View File

@@ -717,6 +717,9 @@ func (r *bufByteReader) ReadByte() (byte, error) {
// It's recommended to supply a reader that buffers and implements io.ByteReader,
// as CidFromReader has to do many single-byte reads to decode varints.
// If the argument only implements io.Reader, single-byte Read calls are used instead.
//
// If the Reader is found to yield zero bytes, an io.EOF error is returned directly, in all
// other error cases, an ErrInvalidCid, wrapping the original error, is returned.
func CidFromReader(r io.Reader) (int, Cid, error) {
// 64 bytes is enough for any CIDv0,
// and it's enough for most CIDv1s in practice.
@@ -727,6 +730,11 @@ func CidFromReader(r io.Reader) (int, Cid, error) {
// The varint package wants a io.ByteReader, so we must wrap our io.Reader.
vers, err := varint.ReadUvarint(br)
if err != nil {
if err == io.EOF {
// First-byte read in ReadUvarint errors with io.EOF, so reader has no data.
// Subsequent reads with an EOF will return io.ErrUnexpectedEOF and be wrapped here.
return 0, Undef, err
}
return len(br.dst), Undef, ErrInvalidCid{err}
}

View File

@@ -783,6 +783,30 @@ func TestBadCidInput(t *testing.T) {
}
}
func TestFromReaderNoData(t *testing.T) {
// Reading no data from io.Reader should return io.EOF, not ErrInvalidCid.
n, cid, err := CidFromReader(bytes.NewReader(nil))
if err != io.EOF {
t.Fatal("Expected io.EOF error")
}
if cid != Undef {
t.Fatal("Expected Undef CID")
}
if n != 0 {
t.Fatal("Expected 0 data")
}
// Read byte indicatiing more data to and check error is ErrInvalidCid.
_, _, err = CidFromReader(bytes.NewReader([]byte{0x80}))
if !errors.Is(err, ErrInvalidCid{}) {
t.Fatal("Expected ErrInvalidCid error")
}
// Check for expected wrapped error.
if !errors.Is(err, io.ErrUnexpectedEOF) {
t.Fatal("Expected error", io.ErrUnexpectedEOF)
}
}
func TestBadParse(t *testing.T) {
hash, err := mh.Sum([]byte("foobar"), mh.SHA3_256, -1)
if err != nil {

View File

@@ -1,3 +1,3 @@
{
"version": "v0.4.0"
"version": "v0.4.1"
}