aboutsummaryrefslogtreecommitdiff
path: root/refstore/reftable/table.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-04 08:26:56 +0800
committerGravatar Runxi Yu2026-03-04 08:59:53 +0800
commitab7501be34032fb9e5c48726a68ae90a917af9eb (patch)
tree20d005647569befea8133e953c3270e8fd2a2a5b /refstore/reftable/table.go
parent*: gofumpt (diff)
signatureNo signature
*: Lint
Diffstat (limited to 'refstore/reftable/table.go')
-rw-r--r--refstore/reftable/table.go59
1 files changed, 54 insertions, 5 deletions
diff --git a/refstore/reftable/table.go b/refstore/reftable/table.go
index 35982bf9..5c05a633 100644
--- a/refstore/reftable/table.go
+++ b/refstore/reftable/table.go
@@ -71,49 +71,69 @@ func openTableFile(root *os.Root, name string, algo objectid.Algorithm) (*tableF
if err != nil {
return nil, err
}
+
info, err := file.Stat()
if err != nil {
_ = file.Close()
+
return nil, err
}
+
size := info.Size()
if size < 0 || size > int64(int(^uint(0)>>1)) {
_ = file.Close()
+
return nil, fmt.Errorf("refstore/reftable: table %q has unsupported size", name)
}
+
fd, err := intconv.UintptrToInt(file.Fd())
if err != nil {
_ = file.Close()
+
return nil, err
}
+
data, err := syscall.Mmap(fd, 0, int(size), syscall.PROT_READ, syscall.MAP_PRIVATE)
if err != nil {
_ = file.Close()
+
return nil, err
}
+
out := &tableFile{name: name, algo: algo, file: file, data: data}
- if err := out.parseMeta(); err != nil {
+
+ err = out.parseMeta()
+ if err != nil {
_ = out.close()
+
return nil, err
}
+
return out, nil
}
// close unmaps and closes one table file.
func (table *tableFile) close() error {
var closeErr error
+
if table.data != nil {
- if err := syscall.Munmap(table.data); err != nil && closeErr == nil {
+ err := syscall.Munmap(table.data)
+ if err != nil && closeErr == nil {
closeErr = err
}
+
table.data = nil
}
+
if table.file != nil {
- if err := table.file.Close(); err != nil && closeErr == nil {
+ err := table.file.Close()
+ if err != nil && closeErr == nil {
closeErr = err
}
+
table.file = nil
}
+
return closeErr
}
@@ -122,9 +142,11 @@ func (table *tableFile) parseMeta() error {
if len(table.data) < 24 {
return fmt.Errorf("refstore/reftable: table %q: file too short", table.name)
}
+
if string(table.data[:4]) != reftableMagic {
return fmt.Errorf("refstore/reftable: table %q: bad magic", table.name)
}
+
version := table.data[4]
switch version {
case version1:
@@ -137,35 +159,47 @@ func (table *tableFile) parseMeta() error {
if len(table.data) < table.headerLen {
return fmt.Errorf("refstore/reftable: table %q: truncated header", table.name)
}
+
hashID := binary.BigEndian.Uint32(table.data[24:28])
- if err := validateHashID(hashID, table.algo); err != nil {
+
+ err := validateHashID(hashID, table.algo)
+ if err != nil {
return fmt.Errorf("refstore/reftable: table %q: %w", table.name, err)
}
default:
return fmt.Errorf("refstore/reftable: table %q: unsupported version %d", table.name, version)
}
+
table.blockSize = int(readUint24(table.data[5:8]))
footerLen := 68
if version == version2 {
footerLen = 72
}
+
if len(table.data) < footerLen {
return fmt.Errorf("refstore/reftable: table %q: missing footer", table.name)
}
+
footerStart := len(table.data) - footerLen
+
footer := table.data[footerStart:]
if string(footer[:4]) != reftableMagic || footer[4] != version {
return fmt.Errorf("refstore/reftable: table %q: invalid footer header", table.name)
}
+
wantCRC := binary.BigEndian.Uint32(footer[footerLen-4:])
+
haveCRC := crc32.ChecksumIEEE(footer[:footerLen-4])
if wantCRC != haveCRC {
return fmt.Errorf("refstore/reftable: table %q: footer crc mismatch", table.name)
}
+
if version == version2 {
hashID := binary.BigEndian.Uint32(footer[24:28])
- if err := validateHashID(hashID, table.algo); err != nil {
+
+ err := validateHashID(hashID, table.algo)
+ if err != nil {
return fmt.Errorf("refstore/reftable: table %q: %w", table.name, err)
}
}
@@ -188,34 +222,44 @@ func (table *tableFile) parseMeta() error {
if err != nil {
return fmt.Errorf("refstore/reftable: table %q: invalid footer offset: %w", table.name, err)
}
+
if table.refIndexPos != 0 && table.refIndexPos < refEnd {
refEnd = table.refIndexPos
}
+
if objPos != 0 && objPos < refEnd {
refEnd = objPos
}
+
if logPos != 0 && logPos < refEnd {
refEnd = logPos
}
+
headerLenU64, err := intconv.IntToUint64(table.headerLen)
if err != nil {
return fmt.Errorf("refstore/reftable: table %q: invalid header length: %w", table.name, err)
}
+
dataLenU64, err := intconv.IntToUint64(len(table.data))
if err != nil {
return fmt.Errorf("refstore/reftable: table %q: invalid data length: %w", table.name, err)
}
+
if refEnd < headerLenU64 || refEnd > dataLenU64 {
return fmt.Errorf("refstore/reftable: table %q: invalid ref section", table.name)
}
+
if table.refIndexPos > dataLenU64 {
return fmt.Errorf("refstore/reftable: table %q: invalid ref index position", table.name)
}
+
refEndInt, err := intconv.Uint64ToInt(refEnd)
if err != nil {
return fmt.Errorf("refstore/reftable: table %q: invalid ref section end: %w", table.name, err)
}
+
table.refEnd = refEndInt
+
return nil
}
@@ -226,11 +270,13 @@ func validateHashID(hashID uint32, algo objectid.Algorithm) error {
if algo != objectid.AlgorithmSHA1 {
return errors.New("hash id sha1 mismatch")
}
+
return nil
case hashIDSHA256:
if algo != objectid.AlgorithmSHA256 {
return errors.New("hash id s256 mismatch")
}
+
return nil
default:
return fmt.Errorf("unknown hash id 0x%08x", hashID)
@@ -242,11 +288,14 @@ func (record recordValue) toRef(name string) (ref.Ref, error) {
if record.deleted {
return nil, errors.New("refstore/reftable: cannot materialize deleted record")
}
+
if record.symbolicTarget != "" {
return ref.Symbolic{RefName: name, Target: record.symbolicTarget}, nil
}
+
if !record.hasDetached {
return nil, errors.New("refstore/reftable: malformed detached record")
}
+
return ref.Detached{RefName: name, ID: record.detachedID, Peeled: record.peeled}, nil
}