shithub: furgit

Download patch

ref: d314d1f7e933ca83081eec289aa0cb6e75a7eeac
parent: 8f577284f47f699855dcb3ceda21aa9d8be77c2f
author: Runxi Yu <runxiyu@umich.edu>
date: Sun Mar 22 13:33:27 EDT 2026

objectstore{,/packed}: Document lifetime and integrity behavior

--- a/objectstore/objectstore.go
+++ b/objectstore/objectstore.go
@@ -19,6 +19,9 @@
 var ErrObjectNotFound = errors.New("objectstore: object not found")
 
 // Store reads Git objects by object ID.
+//
+// Unless an implementation explicitly documents otherwise, values returned by
+// Store methods are only valid until the store is closed.
 type Store interface {
 	// ReadBytesFull reads a full serialized object as "type size\0content".
 	//
@@ -39,6 +42,7 @@
 	// ReadReaderFull reads a full serialized object stream as "type size\0content".
 	//
 	// Caller must close the returned reader.
+	// The returned reader is only valid until the store is closed.
 	//
 	// Any read-time integrity verification performed while producing the stream
 	// is implementation-defined.
@@ -48,6 +52,7 @@
 	// and content stream.
 	//
 	// Caller must close the returned reader.
+	// The returned reader is only valid until the store is closed.
 	//
 	// Any read-time integrity verification performed while producing the stream
 	// is implementation-defined.
--- a/objectstore/packed/close.go
+++ b/objectstore/packed/close.go
@@ -1,16 +1,10 @@
 package packed
 
 // Close releases mapped pack/index resources associated with the store.
+//
+// Repeated calls to Close are undefined behavior.
 func (store *Store) Close() error {
 	store.stateMu.Lock()
-
-	if store.closed {
-		store.stateMu.Unlock()
-
-		return nil
-	}
-
-	store.closed = true
 	root := store.root
 	packs := store.packs
 	store.stateMu.Unlock()
--- a/objectstore/packed/read_header.go
+++ b/objectstore/packed/read_header.go
@@ -6,6 +6,10 @@
 )
 
 // ReadHeader reads an object's type and declared content size.
+//
+// It resolves header metadata only. It does not verify that the full pack entry
+// payload is readable and does not verify any zlib Adler-32 trailer for
+// compressed entry data.
 func (store *Store) ReadHeader(id objectid.ObjectID) (objecttype.Type, int64, error) {
 	loc, err := store.lookup(id)
 	if err != nil {
--- a/objectstore/packed/read_reader.go
+++ b/objectstore/packed/read_reader.go
@@ -12,9 +12,18 @@
 	packfmt "codeberg.org/lindenii/furgit/packfile"
 )
 
-// ReadReaderContent reads an object's type, declared content size, and content stream.
+// ReadReaderContent reads an object's type, declared content size, and content
+// stream.
 //
 // The caller must close the returned reader.
+//
+// For base pack entries, the returned reader borrows store-owned mapped pack
+// data and is only valid until the store is closed.
+//
+// Close releases reader-local resources only. It does not drain unread data for
+// additional validation. In particular, malformed trailing compressed data,
+// trailing bytes past the declared object size, and the zlib Adler-32 trailer
+// may go unverified unless the caller reads to io.EOF.
 func (store *Store) ReadReaderContent(id objectid.ObjectID) (objecttype.Type, int64, io.ReadCloser, error) {
 	loc, err := store.lookup(id)
 	if err != nil {
@@ -49,6 +58,14 @@
 // ReadReaderFull reads a full serialized object stream as "type size\0content".
 //
 // The caller must close the returned reader.
+//
+// For base pack entries, the returned reader borrows store-owned mapped pack
+// data and is only valid until the store is closed.
+//
+// Close releases reader-local resources only. It does not drain unread data for
+// additional validation. In particular, malformed trailing compressed data,
+// trailing bytes past the declared object size, and the zlib Adler-32 trailer
+// may go unverified unless the caller reads to io.EOF.
 func (store *Store) ReadReaderFull(id objectid.ObjectID) (io.ReadCloser, error) {
 	loc, err := store.lookup(id)
 	if err != nil {
--- a/objectstore/packed/read_size.go
+++ b/objectstore/packed/read_size.go
@@ -9,6 +9,10 @@
 )
 
 // ReadSize reads an object's declared content size.
+//
+// Like ReadHeader, it resolves header metadata only. It does not verify that
+// the full pack entry payload is readable and does not verify any zlib
+// Adler-32 trailer for compressed entry data.
 func (store *Store) ReadSize(id objectid.ObjectID) (int64, error) {
 	loc, err := store.lookup(id)
 	if err != nil {
--- a/objectstore/packed/read_test.go
+++ b/objectstore/packed/read_test.go
@@ -177,11 +177,6 @@
 		if err != nil {
 			t.Fatalf("Close: %v", err)
 		}
-
-		err = store.Close()
-		if err != nil {
-			t.Fatalf("Close second: %v", err)
-		}
 	})
 }
 
--- a/objectstore/packed/store.go
+++ b/objectstore/packed/store.go
@@ -46,8 +46,6 @@
 	packs map[string]*packFile
 	// deltaCache caches resolved base objects by pack location.
 	deltaCache *deltaCache
-	// closed reports whether Close has been called.
-	closed bool
 }
 
 var _ objectstore.Store = (*Store)(nil)
--