aboutsummaryrefslogtreecommitdiff
path: root/object
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-28 16:13:55 +0000
committerGravatar Runxi Yu2026-03-28 16:13:55 +0000
commitd3cfc1932e71994ec866f6bea67615c58878f952 (patch)
treeb2e05e30b4b84423d51bd2a9dbdd49cea257062e /object
parentobject/store: Document writing interface lack (diff)
signatureNo signature
object/store/packed: Expect length and verify Adler-32
Diffstat (limited to 'object')
-rw-r--r--object/store/loose/read_bytes.go6
-rw-r--r--object/store/packed/entry_inflate.go9
-rw-r--r--object/store/packed/read_bytes.go8
3 files changed, 23 insertions, 0 deletions
diff --git a/object/store/loose/read_bytes.go b/object/store/loose/read_bytes.go
index 0b6da81b..87a9f020 100644
--- a/object/store/loose/read_bytes.go
+++ b/object/store/loose/read_bytes.go
@@ -29,6 +29,9 @@ func (store *Store) readBytesParsed(id objectid.ObjectID) ([]byte, objecttype.Ty
}
// ReadBytesFull reads a full serialized object as "type size\0content".
+//
+// It inflates and parses the full loose object, including consuming the zlib
+// stream through its Adler-32 trailer.
func (store *Store) ReadBytesFull(id objectid.ObjectID) ([]byte, error) {
raw, _, _, err := store.readBytesParsed(id)
if err != nil {
@@ -39,6 +42,9 @@ func (store *Store) ReadBytesFull(id objectid.ObjectID) ([]byte, error) {
}
// ReadBytesContent reads an object's type and content bytes.
+//
+// Like ReadBytesFull, it inflates and parses the full loose object, including
+// consuming the zlib stream through its Adler-32 trailer.
func (store *Store) ReadBytesContent(id objectid.ObjectID) (objecttype.Type, []byte, error) {
_, ty, content, err := store.readBytesParsed(id)
if err != nil {
diff --git a/object/store/packed/entry_inflate.go b/object/store/packed/entry_inflate.go
index 1c3943e9..f79d86c0 100644
--- a/object/store/packed/entry_inflate.go
+++ b/object/store/packed/entry_inflate.go
@@ -7,6 +7,7 @@ import (
"math"
"codeberg.org/lindenii/furgit/internal/compress/zlib"
+ "codeberg.org/lindenii/furgit/internal/iolimit"
)
// zlibReaderAt opens a zlib reader starting at data offset within pack.
@@ -36,6 +37,7 @@ func inflateAt(pack *packFile, offset int, expectedSize int64) ([]byte, error) {
)
}
+ reader := iolimit.ExpectLengthReader(reader, expectedSize)
body := make([]byte, int(expectedSize))
_, err := io.ReadFull(reader, body)
@@ -43,6 +45,13 @@ func inflateAt(pack *packFile, offset int, expectedSize int64) ([]byte, error) {
return nil, err
}
+ var probe [1]byte
+
+ _, err = reader.Read(probe[:])
+ if err != nil && err != io.EOF {
+ return nil, err
+ }
+
return body, nil
}
diff --git a/object/store/packed/read_bytes.go b/object/store/packed/read_bytes.go
index 333cfaae..98ae6995 100644
--- a/object/store/packed/read_bytes.go
+++ b/object/store/packed/read_bytes.go
@@ -9,6 +9,10 @@ import (
)
// ReadBytesContent reads an object's type and content bytes.
+//
+// It fully resolves the requested object bytes. For base pack entries, this
+// includes verifying that the zlib stream inflates to exactly the declared
+// object size and reaches its Adler-32 trailer.
func (store *Store) ReadBytesContent(id objectid.ObjectID) (objecttype.Type, []byte, error) {
loc, err := store.lookup(id)
if err != nil {
@@ -19,6 +23,10 @@ func (store *Store) ReadBytesContent(id objectid.ObjectID) (objecttype.Type, []b
}
// ReadBytesFull reads a full serialized object as "type size\0content".
+//
+// Like ReadBytesContent, it fully resolves the requested object bytes. For
+// base pack entries, this includes verifying that the zlib stream inflates to
+// exactly the declared object size and reaches its Adler-32 trailer.
func (store *Store) ReadBytesFull(id objectid.ObjectID) ([]byte, error) {
ty, content, err := store.ReadBytesContent(id)
if err != nil {