diff --git a/low/inode.go b/low/inode.go index de52d1d..7a1b03c 100644 --- a/low/inode.go +++ b/low/inode.go @@ -7,7 +7,9 @@ import ( "github.com/CalebQ42/squashfs/low/inode" ) -func (r Reader) InodeFromRef(ref uint64) (inode.Inode, error) { +type InodeRef = uint64 + +func (r Reader) InodeFromRef(ref InodeRef) (inode.Inode, error) { offset, meta := (ref>>16)+r.Superblock.InodeTableStart, ref&0xFFFF rdr := metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d) defer rdr.Close() diff --git a/low/reader.go b/low/reader.go index 5444c27..ec15689 100644 --- a/low/reader.go +++ b/low/reader.go @@ -34,7 +34,7 @@ type Reader struct { d decompress.Decompressor fragTable *Table[fragEntry] idTable *Table[uint32] - exportTable *Table[uint64] + exportTable *Table[InodeRef] } func NewReader(r io.ReaderAt) (rdr Reader, err error) { @@ -78,12 +78,42 @@ func NewReader(r io.ReaderAt) (rdr Reader, err error) { if err != nil { return rdr, errors.Join(errors.New("failed to read root directory"), err) } - rdr.fragTable = NewTable[fragEntry](&rdr, rdr.Superblock.FragTableStart, rdr.Superblock.FragCount) - rdr.idTable = NewTable[uint32](&rdr, rdr.Superblock.IdTableStart, uint32(rdr.Superblock.IdCount)) - rdr.exportTable = NewTable[uint64](&rdr, rdr.Superblock.ExportTableStart, rdr.Superblock.InodeCount) + rdr.fragTable = NewTable(&rdr, rdr.Superblock.FragTableStart, rdr.Superblock.FragCount, readFrag) + rdr.idTable = NewTable(&rdr, rdr.Superblock.IdTableStart, uint32(rdr.Superblock.IdCount), readId) + rdr.exportTable = NewTable(&rdr, rdr.Superblock.ExportTableStart, rdr.Superblock.InodeCount, readRef) return } +func readFrag(r io.Reader) (fragEntry, error) { + dat := make([]byte, 16) + _, err := r.Read(dat) + if err != nil { + return fragEntry{}, err + } + return fragEntry{ + Start: binary.LittleEndian.Uint64(dat[0:8]), + Size: binary.LittleEndian.Uint32(dat[8:12]), + }, nil +} + +func readId(r io.Reader) (uint32, error) { + dat := make([]byte, 4) + _, err := r.Read(dat) + if err != nil { + return 0, err + } + return binary.LittleEndian.Uint32(dat), nil +} + +func readRef(r io.Reader) (InodeRef, error) { + dat := make([]byte, 8) + _, err := r.Read(dat) + if err != nil { + return 0, err + } + return binary.LittleEndian.Uint64(dat), nil +} + // Get a uid/gid at the given index. Lazily populates the reader's Id table as necessary. func (r *Reader) Id(i uint16) (uint32, error) { return r.idTable.Get(uint32(i)) @@ -95,7 +125,7 @@ func (r *Reader) fragEntry(i uint32) (fragEntry, error) { } // Get an inode reference at the given index. Lazily populates the reader's export table as necessary. -func (r *Reader) inodeRef(i uint32) (uint64, error) { +func (r *Reader) inodeRef(i uint32) (InodeRef, error) { return r.exportTable.Get(i) } diff --git a/low/superblock.go b/low/superblock.go index dca087a..48e9d74 100644 --- a/low/superblock.go +++ b/low/superblock.go @@ -14,7 +14,7 @@ type superblock struct { IdCount uint16 VerMaj uint16 VerMin uint16 - RootInodeRef uint64 + RootInodeRef InodeRef Size uint64 IdTableStart uint64 XattrTableStart uint64 diff --git a/low/table.go b/low/table.go index 986eb0a..cf90a40 100644 --- a/low/table.go +++ b/low/table.go @@ -3,6 +3,7 @@ package squashfslow import ( "encoding/binary" "errors" + "io" "sync" "github.com/CalebQ42/squashfs/internal/metadata" @@ -13,6 +14,8 @@ var errOutOfBounds = errors.New("out of bounds") var errUnexpectedOutOfBounds = errors.New("unexpected out of bounds") var errNilCollection = errors.New("nil collection") +type CreateFunction[T any] = func(io.Reader) (T, error) + type Table[T any] struct { totalItems uint32 itemsPerBlock uint32 @@ -20,9 +23,10 @@ type Table[T any] struct { mut sync.RWMutex currentItems []T rdr *Reader + createFunc CreateFunction[T] } -func NewTable[T any](rdr *Reader, start uint64, totalItems uint32) *Table[T] { +func NewTable[T any](rdr *Reader, start uint64, totalItems uint32, createFunc CreateFunction[T]) *Table[T] { var zero T return &Table[T]{ totalItems: totalItems, @@ -30,6 +34,7 @@ func NewTable[T any](rdr *Reader, start uint64, totalItems uint32) *Table[T] { offset: start, mut: sync.RWMutex{}, rdr: rdr, + createFunc: createFunc, } } @@ -65,14 +70,16 @@ func (t *Table[T]) fillAndGet(requestedItemIndex uint32) (T, error) { } t.offset += 8 toRead = min(t.itemsPerBlock, t.totalItems-uint32(len(t.currentItems))) - new := make([]T, toRead) + oldLen := uint32(len(t.currentItems)) + t.currentItems = append(t.currentItems, make([]T, toRead)...) metaRdr = metadata.NewReader(toreader.NewReader(t.rdr.r, int64(offset)), t.rdr.d) - err = binary.Read(&metaRdr, binary.LittleEndian, new) - if err != nil { - var zero T - return zero, err + for i := range toRead { + t.currentItems[oldLen+i], err = t.createFunc(&metaRdr) + if err != nil { + var zero T + return zero, err + } } - t.currentItems = append(t.currentItems, new...) } return t.currentItems[requestedItemIndex], nil }