diff options
| author | 2026-03-04 08:26:56 +0800 | |
|---|---|---|
| committer | 2026-03-04 08:59:53 +0800 | |
| commit | ab7501be34032fb9e5c48726a68ae90a917af9eb (patch) | |
| tree | 20d005647569befea8133e953c3270e8fd2a2a5b /refstore/reftable/table.go | |
| parent | *: gofumpt (diff) | |
| signature | No signature | |
*: Lint
Diffstat (limited to 'refstore/reftable/table.go')
| -rw-r--r-- | refstore/reftable/table.go | 59 |
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 } |
