github.com/quay/claircore@v1.5.28/rpm/ndb/ndb.go (about) 1 package ndb 2 3 import ( 4 "context" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "io" 9 10 "github.com/quay/claircore/rpm/internal/rpm" 11 ) 12 13 var le = binary.LittleEndian 14 15 // Used throughout the various DBs. 16 const ( 17 slotSize = 4 * 4 18 slotStart = 2 19 ) 20 21 // CheckMagic reports whether the Reader starts with a magic header for 22 // a file format supported by this package. 23 func CheckMagic(_ context.Context, r io.Reader) bool { 24 const ( 25 xdb = 'R' | 'p'<<8 | 'm'<<16 | 'X'<<24 26 pkg = 'R' | 'p'<<8 | 'm'<<16 | 'P'<<24 27 ) 28 b := make([]byte, 4) 29 if _, err := io.ReadFull(r, b); err != nil { 30 return false 31 } 32 m := le.Uint32(b) 33 return m == xdb || m == pkg 34 } 35 36 // XDB is the "xdb" a.k.a. "Index.db", the ndb mechanism for creating indexes. 37 type XDB struct { 38 r io.ReaderAt 39 lookup map[rpm.Tag]*xdbSlot 40 slot []xdbSlot 41 xdbHeader 42 } 43 44 // Parse closes over the passed [io.ReaderAt] and populates the XDB. 45 func (db *XDB) Parse(r io.ReaderAt) error { 46 const headerSize = 32 47 h := make([]byte, headerSize) 48 if _, err := r.ReadAt(h, 0); err != nil { 49 return fmt.Errorf("xdb: unable to read header: %w", err) 50 } 51 if err := db.xdbHeader.UnmarshalBinary(h); err != nil { 52 return fmt.Errorf("xdb: bad header: %w", err) 53 } 54 pg := make([]byte, db.PageSize*db.SlotNPages) 55 if _, err := r.ReadAt(pg, 0); err != nil { 56 return fmt.Errorf("xdb: unable to read slots: %w", err) 57 } 58 59 // Size for full pages of slots. 60 max := (len(pg) / slotSize) - slotStart 61 db.lookup = make(map[rpm.Tag]*xdbSlot, max) 62 db.slot = make([]xdbSlot, max) 63 n := 0 64 var x *xdbSlot 65 for off := slotStart * slotSize; n < max; n, off = n+1, off+slotSize { 66 x = &db.slot[n] 67 if err := x.UnmarshalBinary(pg[off:]); err != nil { 68 return err 69 } 70 if x.Tag == 0 || x.Tag == rpm.TagInvalid { 71 break 72 } 73 db.lookup[x.Tag] = x 74 } 75 db.slot = db.slot[:n] 76 db.r = r 77 return nil 78 } 79 80 // Index reports the index for the specifed tag. 81 func (db *XDB) Index(tag rpm.Tag) (*Index, error) { 82 slot, ok := db.lookup[tag] 83 if !ok { 84 return nil, fmt.Errorf("ndb: no such tag %d", tag) 85 } 86 off, ct := int64(slot.StartPage*db.PageSize), int64(slot.PageCount*db.PageSize) 87 r := io.NewSectionReader(db.r, off, ct) 88 var idx Index 89 if err := idx.Parse(r); err != nil { 90 return nil, err 91 } 92 return &idx, nil 93 } 94 95 type xdbHeader struct { 96 Version uint32 97 Generation uint32 98 SlotNPages uint32 99 PageSize uint32 100 UserGeneration uint32 101 } 102 103 // UnmarshalBinary implements encoding.BinaryUnmarshaler for the xdb header. 104 func (h *xdbHeader) UnmarshalBinary(b []byte) error { 105 const ( 106 headerSz = 32 107 magic = 'R' | 'p'<<8 | 'm'<<16 | 'X'<<24 108 version = 0 109 110 offsetMagic = 0 111 offsetVersion = 4 112 offsetGeneration = 8 113 offsetSlotNPages = 12 114 offsetPageSize = 16 115 offsetUserGeneration = 20 116 ) 117 118 if len(b) < headerSz { 119 return io.ErrShortBuffer 120 } 121 if le.Uint32(b[offsetMagic:]) != magic { 122 return errors.New("xdb: bad magic") 123 } 124 h.Version = le.Uint32(b[offsetVersion:]) 125 if h.Version != version { 126 return errors.New("bad version") 127 } 128 h.Generation = le.Uint32(b[offsetGeneration:]) 129 h.SlotNPages = le.Uint32(b[offsetSlotNPages:]) 130 h.PageSize = le.Uint32(b[offsetPageSize:]) 131 h.UserGeneration = le.Uint32(b[offsetUserGeneration:]) 132 return nil 133 } 134 135 type xdbSlot struct { 136 Subtag uint8 137 Tag rpm.Tag 138 StartPage uint32 139 PageCount uint32 140 } 141 142 func (s *xdbSlot) UnmarshalBinary(b []byte) error { 143 const ( 144 magic = ('S' | 'l'<<8 | 'o'<<16 | 0x00<<24) 145 magicMask = ^uint32(0xFF << 24) 146 147 magicOffset = 0 148 subtagOffset = 3 149 tagOffset = 4 150 startOffset = 8 151 countOffset = 12 152 ) 153 if len(b) < slotSize { 154 return io.ErrShortBuffer 155 } 156 if le.Uint32(b[magicOffset:])&magicMask != magic { 157 return fmt.Errorf("slot: bad magic") 158 } 159 s.Subtag = b[subtagOffset] 160 s.Tag = rpm.Tag(le.Uint32(b[tagOffset:])) 161 s.StartPage = le.Uint32(b[startOffset:]) 162 s.PageCount = le.Uint32(b[countOffset:]) 163 return nil 164 }