Merge branch 'main' into upstream-fix-out-of-range-panic

This commit is contained in:
Caleb Gardner
2025-06-06 14:26:10 -05:00
committed by GitHub
28 changed files with 743 additions and 322 deletions
+107 -21
View File
@@ -1,15 +1,14 @@
package data
import (
"encoding/binary"
"errors"
"io"
"io/fs"
"math"
"runtime"
"sync"
"github.com/CalebQ42/squashfs/internal/decompress"
"github.com/CalebQ42/squashfs/internal/toreader"
)
type FragReaderConstructor func() (io.Reader, error)
@@ -18,16 +17,16 @@ type FullReader struct {
r io.ReaderAt
d decompress.Decompressor
frag FragReaderConstructor
retPool *sync.Pool
sizes []uint32
initialOffset int64
finalBlockSize uint64
blockSize uint32
goroutineLimit uint16
closed bool
}
func NewFullReader(r io.ReaderAt, initialOffset int64, d decompress.Decompressor, sizes []uint32, finalBlockSize uint64, blockSize uint32) *FullReader {
return &FullReader{
func NewFullReader(r io.ReaderAt, initialOffset int64, d decompress.Decompressor, sizes []uint32, finalBlockSize uint64, blockSize uint32) FullReader {
return FullReader{
r: r,
d: d,
sizes: sizes,
@@ -35,19 +34,26 @@ func NewFullReader(r io.ReaderAt, initialOffset int64, d decompress.Decompressor
goroutineLimit: uint16(runtime.NumCPU()),
finalBlockSize: finalBlockSize,
blockSize: blockSize,
retPool: &sync.Pool{
New: func() any {
return &retValue{}
},
},
}
}
func (r *FullReader) Close() error {
r.closed = true
r.r = nil
r.d = nil
r.frag = nil
r.sizes = nil
return nil
}
func (r *FullReader) AddFrag(frag FragReaderConstructor) {
r.frag = frag
}
func (r *FullReader) SetGoroutineLimit(limit uint16) {
if limit <= 0 {
r.goroutineLimit = 1
}
r.goroutineLimit = limit
}
@@ -57,8 +63,8 @@ type retValue struct {
index uint64
}
func (r *FullReader) process(index uint64, fileOffset uint64, retChan chan *retValue) {
ret := r.retPool.Get().(*retValue)
func (r FullReader) process(index uint64, fileOffset uint64, pool *sync.Pool, retChan chan *retValue) {
ret := pool.Get().(*retValue)
ret.index = index
realSize := r.sizes[index] &^ (1 << 24)
if realSize == 0 {
@@ -72,14 +78,20 @@ func (r *FullReader) process(index uint64, fileOffset uint64, retChan chan *retV
return
}
ret.data = make([]byte, realSize)
ret.err = binary.Read(toreader.NewReader(r.r, int64(r.initialOffset)+int64(fileOffset)), binary.LittleEndian, &ret.data)
_, ret.err = r.r.ReadAt(ret.data, r.initialOffset+int64(fileOffset))
if r.sizes[index] == realSize {
ret.data, ret.err = r.d.Decompress(ret.data)
}
retChan <- ret
}
func (r *FullReader) WriteTo(w io.Writer) (int64, error) {
func (r FullReader) WriteTo(w io.Writer) (int64, error) {
if r.closed {
return 0, fs.ErrClosed
}
// if wa, is := w.(io.WriterAt); is {
// return r.writeToWriteAt(wa)
// }
var curIndex uint64
var curOffset uint64
var toProcess uint16
@@ -87,14 +99,16 @@ func (r *FullReader) WriteTo(w io.Writer) (int64, error) {
cache := make(map[uint64]*retValue)
var errCache []error
retChan := make(chan *retValue, r.goroutineLimit)
pool := &sync.Pool{
New: func() any {
return &retValue{}
},
}
for i := uint64(0); i < uint64(math.Ceil(float64(len(r.sizes))/float64(r.goroutineLimit))); i++ {
toProcess = uint16(len(r.sizes)) - (uint16(i) * r.goroutineLimit)
if toProcess > r.goroutineLimit {
toProcess = r.goroutineLimit
}
toProcess = min(uint16(len(r.sizes))-(uint16(i)*r.goroutineLimit), r.goroutineLimit)
// Start all the goroutines
for j := uint16(0); j < toProcess; j++ {
go r.process((i*uint64(r.goroutineLimit))+uint64(j), curOffset, retChan)
go r.process((i*uint64(r.goroutineLimit))+uint64(j), curOffset, pool, retChan)
curOffset += uint64(r.sizes[(i*uint64(r.goroutineLimit))+uint64(j)]) &^ (1 << 24)
}
// Then consume the results on retChan
@@ -128,7 +142,7 @@ func (r *FullReader) WriteTo(w io.Writer) (int64, error) {
}
continue
}
r.retPool.Put(res)
pool.Put(res)
curIndex++
// Now we recursively try to clear the cache
for len(cache) > 0 {
@@ -146,7 +160,7 @@ func (r *FullReader) WriteTo(w io.Writer) (int64, error) {
break
}
delete(cache, curIndex)
r.retPool.Put(res)
pool.Put(res)
curIndex++
}
}
@@ -172,3 +186,75 @@ func (r *FullReader) WriteTo(w io.Writer) (int64, error) {
}
return wrote, nil
}
// func (r FullReader) writeToWriteAt(w io.WriterAt) (out int64, outErr error) {
// wait := &sync.WaitGroup{}
// wait.Add(len(r.sizes))
// mgr := routinemanager.NewManager(r.goroutineLimit)
// curOffset := r.initialOffset
// for i := uint64(0); i < uint64(len(r.sizes)); i++ {
// go func(index uint64, fileOffset int64) {
// lckNum := mgr.Lock()
// defer mgr.Unlock(lckNum)
// defer wait.Done()
// realSize := r.sizes[index] &^ (1 << 24)
// if realSize == 0 {
// if index == uint64(len(r.sizes))-1 && r.frag == nil {
// _, err := w.WriteAt([]byte{0}, int64((uint64(r.blockSize)*index)+r.finalBlockSize)-1)
// if err != nil {
// outErr = errors.Join(outErr, err)
// return
// }
// out = max(out, int64((uint64(r.blockSize)*index)+r.finalBlockSize))
// }
// return
// }
// data := make([]byte, realSize)
// err := binary.Read(toreader.NewReader(r.r, int64(fileOffset)), binary.LittleEndian, &data)
// if err != nil {
// outErr = errors.Join(outErr, err)
// return
// }
// if r.sizes[index] == realSize {
// data, err = r.d.Decompress(data)
// }
// if err != nil {
// outErr = errors.Join(outErr, err)
// return
// }
// _, err = w.WriteAt(data, int64(uint64(r.blockSize)*index))
// if err != nil {
// outErr = errors.Join(outErr, err)
// return
// }
// out = max(out, int64(uint64(r.blockSize)*(index+1)))
// }(i, curOffset)
// curOffset += int64(r.sizes[i]) &^ (1 << 24)
// }
// if r.frag != nil {
// wait.Add(1)
// go func() {
// lckNum := mgr.Lock()
// defer mgr.Unlock(lckNum)
// defer wait.Done()
// rdr, err := r.frag()
// if err != nil {
// outErr = errors.Join(outErr, err)
// return
// }
// dat, err := io.ReadAll(rdr)
// if err != nil {
// outErr = errors.Join(outErr, err)
// return
// }
// _, err = w.WriteAt(dat, int64(int(r.blockSize)*len(r.sizes)))
// if err != nil {
// outErr = errors.Join(outErr, err)
// return
// }
// out = int64(int(r.blockSize)*len(r.sizes)) + int64(r.finalBlockSize)
// }()
// }
// wait.Wait()
// return
// }
+15 -8
View File
@@ -1,8 +1,8 @@
package data
import (
"encoding/binary"
"io"
"io/fs"
"github.com/CalebQ42/squashfs/internal/decompress"
)
@@ -17,10 +17,11 @@ type Reader struct {
curIndex uint64
finalBlockSize uint64
blockSize uint32
closed bool
}
func NewReader(r io.Reader, d decompress.Decompressor, sizes []uint32, finalBlockSize uint64, blockSize uint32) *Reader {
return &Reader{
func NewReader(r io.Reader, d decompress.Decompressor, sizes []uint32, finalBlockSize uint64, blockSize uint32) Reader {
return Reader{
r: r,
d: d,
sizes: sizes,
@@ -41,6 +42,7 @@ func (r *Reader) advance() error {
r.dat, err = io.ReadAll(r.frag)
return err
} else if r.curIndex >= uint64(len(r.sizes)) {
r.dat = []byte{}
return io.EOF
}
realSize := r.sizes[r.curIndex] &^ (1 << 24)
@@ -53,7 +55,7 @@ func (r *Reader) advance() error {
return nil
}
r.dat = make([]byte, realSize)
err = binary.Read(r.r, binary.LittleEndian, &r.dat)
_, err = r.r.Read(r.dat)
if err != nil {
return err
}
@@ -65,6 +67,9 @@ func (r *Reader) advance() error {
}
func (r *Reader) Read(b []byte) (int, error) {
if r.closed {
return 0, fs.ErrClosed
}
curRead := 0
var toRead int
for curRead < len(b) {
@@ -73,10 +78,7 @@ func (r *Reader) Read(b []byte) (int, error) {
return curRead, err
}
}
toRead = len(b) - curRead
if toRead > len(r.dat)-r.curOffset {
toRead = len(r.dat) - r.curOffset
}
toRead = min(len(b)-curRead, len(r.dat)-r.curOffset)
toRead = copy(b[curRead:], r.dat[r.curOffset:r.curOffset+toRead])
r.curOffset += toRead
curRead += toRead
@@ -85,6 +87,9 @@ func (r *Reader) Read(b []byte) (int, error) {
}
func (r *Reader) Close() error {
r.closed = true
r.r = nil
r.d = nil
if r.frag != nil {
if l, ok := r.frag.(*io.LimitedReader); ok {
if cl, ok := l.R.(io.Closer); ok {
@@ -92,6 +97,8 @@ func (r *Reader) Close() error {
}
}
}
r.frag = nil
r.sizes = nil
r.dat = nil
return nil
}
+3 -3
View File
@@ -18,7 +18,7 @@ type Directory struct {
Entries []directory.Entry
}
func (r *Reader) directoryFromRef(ref uint64, name string) (Directory, error) {
func (r Reader) directoryFromRef(ref uint64, name string) (Directory, error) {
i, err := r.InodeFromRef(ref)
if err != nil {
return Directory{}, err
@@ -44,7 +44,7 @@ func (r *Reader) directoryFromRef(ref uint64, name string) (Directory, error) {
if err != nil {
return Directory{}, err
}
entries, err := directory.ReadDirectory(dirRdr, size)
entries, err := directory.ReadDirectory(&dirRdr, size)
if err != nil {
return Directory{}, err
}
@@ -54,7 +54,7 @@ func (r *Reader) directoryFromRef(ref uint64, name string) (Directory, error) {
}, nil
}
func (d *Directory) Open(r *Reader, path string) (FileBase, error) {
func (d Directory) Open(r Reader, path string) (FileBase, error) {
path = filepath.Clean(path)
if path == "." || path == "" {
return d.FileBase, nil
+21 -21
View File
@@ -16,11 +16,11 @@ type FileBase struct {
Name string
}
func (r *Reader) BaseFromInode(i inode.Inode, name string) FileBase {
func (r Reader) BaseFromInode(i inode.Inode, name string) FileBase {
return FileBase{Inode: i, Name: name}
}
func (r *Reader) BaseFromEntry(e directory.Entry) (FileBase, error) {
func (r Reader) BaseFromEntry(e directory.Entry) (FileBase, error) {
in, err := r.InodeFromEntry(e)
if err != nil {
return FileBase{}, err
@@ -28,7 +28,7 @@ func (r *Reader) BaseFromEntry(e directory.Entry) (FileBase, error) {
return FileBase{Inode: in, Name: e.Name}, nil
}
func (r *Reader) BaseFromRef(ref uint64, name string) (FileBase, error) {
func (r Reader) BaseFromRef(ref uint64, name string) (FileBase, error) {
in, err := r.InodeFromRef(ref)
if err != nil {
return FileBase{}, err
@@ -36,19 +36,19 @@ func (r *Reader) BaseFromRef(ref uint64, name string) (FileBase, error) {
return FileBase{Inode: in, Name: name}, nil
}
func (b *FileBase) Uid(r *Reader) (uint32, error) {
func (b FileBase) Uid(r *Reader) (uint32, error) {
return r.Id(b.Inode.UidInd)
}
func (b *FileBase) Gid(r *Reader) (uint32, error) {
func (b FileBase) Gid(r *Reader) (uint32, error) {
return r.Id(b.Inode.GidInd)
}
func (b *FileBase) IsDir() bool {
func (b FileBase) IsDir() bool {
return b.Inode.Type == inode.Dir || b.Inode.Type == inode.EDir
}
func (b *FileBase) ToDir(r *Reader) (Directory, error) {
func (b FileBase) ToDir(r Reader) (Directory, error) {
var blockStart uint32
var size uint32
var offset uint16
@@ -70,23 +70,23 @@ func (b *FileBase) ToDir(r *Reader) (Directory, error) {
if err != nil {
return Directory{}, err
}
entries, err := directory.ReadDirectory(dirRdr, size)
entries, err := directory.ReadDirectory(&dirRdr, size)
if err != nil {
return Directory{}, err
}
return Directory{
FileBase: *b,
FileBase: b,
Entries: entries,
}, nil
}
func (b *FileBase) IsRegular() bool {
func (b FileBase) IsRegular() bool {
return b.Inode.Type == inode.Fil || b.Inode.Type == inode.EFil
}
func (b *FileBase) GetRegFileReaders(r *Reader) (*data.Reader, *data.FullReader, error) {
func (b FileBase) GetRegFileReaders(r Reader) (data.Reader, data.FullReader, error) {
if !b.IsRegular() {
return nil, nil, errors.New("not a regular file")
return data.Reader{}, data.FullReader{}, errors.New("not a regular file")
}
var blockStart uint64
var fragIndex uint32
@@ -113,13 +113,13 @@ func (b *FileBase) GetRegFileReaders(r *Reader) (*data.Reader, *data.FullReader,
}
frag := data.NewReader(toreader.NewReader(r.r, int64(ent.Start)), r.d, []uint32{ent.Size}, uint64(r.Superblock.BlockSize), r.Superblock.BlockSize)
frag.Read(make([]byte, fragOffset))
return io.LimitReader(frag, int64(fragSize)), nil
return io.LimitReader(&frag, int64(fragSize)), nil
}
outRdr := data.NewReader(toreader.NewReader(r.r, int64(blockStart)), r.d, sizes, fragSize, r.Superblock.BlockSize)
if fragIndex != 0xffffffff {
f, err := frag()
if err != nil {
return nil, nil, err
return data.Reader{}, data.FullReader{}, err
}
outRdr.AddFrag(f)
}
@@ -130,9 +130,9 @@ func (b *FileBase) GetRegFileReaders(r *Reader) (*data.Reader, *data.FullReader,
return outRdr, outFull, nil
}
func (b *FileBase) GetFullReader(r *Reader) (*data.FullReader, error) {
func (b FileBase) GetFullReader(r *Reader) (data.FullReader, error) {
if !b.IsRegular() {
return nil, errors.New("not a regular file")
return data.FullReader{}, errors.New("not a regular file")
}
var blockStart uint64
var fragIndex uint32
@@ -161,15 +161,15 @@ func (b *FileBase) GetFullReader(r *Reader) (*data.FullReader, error) {
}
frag := data.NewReader(toreader.NewReader(r.r, int64(ent.Start)), r.d, []uint32{ent.Size}, uint64(r.Superblock.BlockSize), r.Superblock.BlockSize)
frag.Read(make([]byte, fragOffset))
return io.LimitReader(frag, int64(fragSize)), nil
return io.LimitReader(&frag, int64(fragSize)), nil
})
}
return outFull, nil
}
func (b *FileBase) GetReader(r *Reader) (*data.Reader, error) {
func (b FileBase) GetReader(r *Reader) (data.Reader, error) {
if !b.IsRegular() {
return nil, errors.New("not a regular file")
return data.Reader{}, errors.New("not a regular file")
}
var blockStart uint64
var fragIndex uint32
@@ -193,11 +193,11 @@ func (b *FileBase) GetReader(r *Reader) (*data.Reader, error) {
if fragIndex != 0xffffffff {
ent, err := r.fragEntry(fragIndex)
if err != nil {
return nil, err
return data.Reader{}, err
}
frag := data.NewReader(toreader.NewReader(r.r, int64(ent.Start)), r.d, []uint32{ent.Size}, uint64(r.Superblock.BlockSize), r.Superblock.BlockSize)
frag.Read(make([]byte, fragOffset))
outRdr.AddFrag(io.LimitReader(frag, int64(fragSize)))
outRdr.AddFrag(io.LimitReader(&frag, int64(fragSize)))
}
return outRdr, nil
}
+4 -4
View File
@@ -7,7 +7,7 @@ import (
"github.com/CalebQ42/squashfs/low/inode"
)
func (r *Reader) InodeFromRef(ref uint64) (inode.Inode, error) {
func (r Reader) InodeFromRef(ref uint64) (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()
@@ -15,12 +15,12 @@ func (r *Reader) InodeFromRef(ref uint64) (inode.Inode, error) {
if err != nil {
return inode.Inode{}, err
}
return inode.Read(rdr, r.Superblock.BlockSize)
return inode.Read(&rdr, r.Superblock.BlockSize)
}
func (r *Reader) InodeFromEntry(e directory.Entry) (inode.Inode, error) {
func (r Reader) InodeFromEntry(e directory.Entry) (inode.Inode, error) {
rdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.Superblock.InodeTableStart)+int64(e.BlockStart)), r.d)
defer rdr.Close()
rdr.Read(make([]byte, e.Offset))
return inode.Read(rdr, r.Superblock.BlockSize)
return inode.Read(&rdr, r.Superblock.BlockSize)
}
+32 -19
View File
@@ -13,7 +13,7 @@ type Directory struct {
ParentNum uint32
}
type eDirectoryInit struct {
type EDirectory struct {
LinkCount uint32
Size uint32
BlockStart uint32
@@ -21,42 +21,55 @@ type eDirectoryInit struct {
IndCount uint16
Offset uint16
XattrInd uint32
}
type EDirectory struct {
eDirectoryInit
Indexes []DirectoryIndex
}
type directoryIndexInit struct {
Ind uint32
Start uint32
NameSize uint32
Indexes []DirectoryIndex
}
type DirectoryIndex struct {
directoryIndexInit
Name []byte
Ind uint32
Start uint32
NameSize uint32
Name []byte
}
func ReadDir(r io.Reader) (d Directory, err error) {
err = binary.Read(r, binary.LittleEndian, &d)
dat := make([]byte, 16)
_, err = r.Read(dat)
if err != nil {
return
}
d.BlockStart = binary.LittleEndian.Uint32(dat)
d.LinkCount = binary.LittleEndian.Uint32(dat[4:])
d.Size = binary.LittleEndian.Uint16(dat[8:])
d.Offset = binary.LittleEndian.Uint16(dat[10:])
d.ParentNum = binary.LittleEndian.Uint32(dat[12:])
return
}
func ReadEDir(r io.Reader) (d EDirectory, err error) {
err = binary.Read(r, binary.LittleEndian, &d.eDirectoryInit)
dat := make([]byte, 24)
_, err = r.Read(dat)
if err != nil {
return
}
d.LinkCount = binary.LittleEndian.Uint32(dat)
d.Size = binary.LittleEndian.Uint32(dat[4:])
d.BlockStart = binary.LittleEndian.Uint32(dat[8:])
d.ParentNum = binary.LittleEndian.Uint32(dat[12:])
d.IndCount = binary.LittleEndian.Uint16(dat[16:])
d.Offset = binary.LittleEndian.Uint16(dat[18:])
d.XattrInd = binary.LittleEndian.Uint32(dat[20:])
d.Indexes = make([]DirectoryIndex, d.IndCount)
for i := range d.Indexes {
err = binary.Read(r, binary.LittleEndian, &d.Indexes[i].directoryIndexInit)
for i := range d.IndCount {
dat = make([]byte, 12)
_, err = r.Read(dat)
if err != nil {
return
}
d.Indexes[i].Ind = binary.LittleEndian.Uint32(dat)
d.Indexes[i].Start = binary.LittleEndian.Uint32(dat[4:])
d.Indexes[i].NameSize = binary.LittleEndian.Uint32(dat[8:])
d.Indexes[i].Name = make([]byte, d.Indexes[i].NameSize+1)
err = binary.Read(r, binary.LittleEndian, &d.Indexes[i].Name)
_, err = r.Read(d.Indexes[i].Name)
if err != nil {
return
}
+15 -7
View File
@@ -6,15 +6,11 @@ import (
"math"
)
type fileInit struct {
type File struct {
BlockStart uint32
FragInd uint32
FragOffset uint32
Size uint32
}
type File struct {
fileInit
BlockSizes []uint32
}
@@ -34,16 +30,28 @@ type EFile struct {
}
func ReadFile(r io.Reader, blockSize uint32) (f File, err error) {
err = binary.Read(r, binary.LittleEndian, &f.fileInit)
dat := make([]byte, 16)
_, err = r.Read(dat)
if err != nil {
return
}
f.BlockStart = binary.LittleEndian.Uint32(dat)
f.FragInd = binary.LittleEndian.Uint32(dat[4:])
f.FragOffset = binary.LittleEndian.Uint32(dat[8:])
f.Size = binary.LittleEndian.Uint32(dat[12:])
toRead := int(math.Floor(float64(f.Size) / float64(blockSize)))
if f.FragInd == 0xFFFFFFFF && f.Size%blockSize > 0 {
toRead++
}
dat = make([]byte, toRead*4)
_, err = r.Read(dat)
if err != nil {
return
}
f.BlockSizes = make([]uint32, toRead)
err = binary.Read(r, binary.LittleEndian, &f.BlockSizes)
for i := range toRead {
f.BlockSizes[i] = binary.LittleEndian.Uint32(dat[i*4:])
}
return
}
+7 -13
View File
@@ -81,23 +81,17 @@ func Read(r io.Reader, blockSize uint32) (i Inode, err error) {
func (i Inode) Mode() (out fs.FileMode) {
out = fs.FileMode(i.Perm)
switch i.Data.(type) {
case Directory:
switch i.Type {
case Dir, EDir:
out |= fs.ModeDir
case EDirectory:
out |= fs.ModeDir
case Symlink:
case Sym, ESym:
out |= fs.ModeSymlink
case ESymlink:
out |= fs.ModeSymlink
case Device:
case Char, EChar, Block, EBlock:
out |= fs.ModeDevice
case EDevice:
out |= fs.ModeDevice
case IPC:
out |= fs.ModeNamedPipe
case EIPC:
case Fifo, EFifo:
out |= fs.ModeNamedPipe
case Sock, ESock:
out |= fs.ModeSocket
}
return
}
+30 -29
View File
@@ -39,41 +39,46 @@ type Reader struct {
Superblock superblock
}
func NewReader(r io.ReaderAt) (rdr *Reader, err error) {
rdr = new(Reader)
func NewReader(r io.ReaderAt) (rdr Reader, err error) {
rdr.r = r
err = binary.Read(toreader.NewReader(r, 0), binary.LittleEndian, &rdr.Superblock)
if err != nil {
return nil, errors.Join(errors.New("failed to read superblock"), err)
return rdr, errors.Join(errors.New("failed to read superblock"), err)
}
if !rdr.Superblock.ValidMagic() {
return nil, ErrorMagic
return rdr, ErrorMagic
}
if !rdr.Superblock.ValidBlockLog() {
return nil, ErrorLog
return rdr, ErrorLog
}
if !rdr.Superblock.ValidVersion() {
return nil, ErrorVersion
return rdr, ErrorVersion
}
switch rdr.Superblock.CompType {
case ZlibCompression:
rdr.d = decompress.Zlib{}
rdr.d = decompress.NewZlib()
case LZMACompression:
rdr.d = decompress.Lzma{}
rdr.d, err = decompress.NewLzma()
if err != nil {
return rdr, err
}
case LZOCompression:
rdr.d = decompress.Lzo{}
rdr.d, err = decompress.NewLzo()
if err != nil {
return rdr, err
}
case XZCompression:
rdr.d = decompress.Xz{}
rdr.d = decompress.NewXz()
case LZ4Compression:
rdr.d = decompress.Lz4{}
rdr.d = decompress.NewLz4()
case ZSTDCompression:
rdr.d = &decompress.Zstd{}
rdr.d = decompress.NewZstd()
default:
return nil, errors.New("invalid compression type. possible corrupted archive")
return rdr, errors.New("invalid compression type. possible corrupted archive")
}
rdr.Root, err = rdr.directoryFromRef(rdr.Superblock.RootInodeRef, "")
if err != nil {
return nil, errors.Join(errors.New("failed to read root directory"), err)
return rdr, errors.Join(errors.New("failed to read root directory"), err)
}
return
}
@@ -88,7 +93,7 @@ func (r *Reader) Id(i uint16) (uint32, error) {
// Populate the id table as needed
var blockNum uint32
if i != 0 { // If i == 0, we go negatives causing issues with uint32s
blockNum = uint32(math.Ceil(float64(i)/2048)) - 1
blockNum = uint32(math.Ceil(float64(i+1)/2048)) - 1
} else {
blockNum = 0
}
@@ -99,19 +104,17 @@ func (r *Reader) Id(i uint16) (uint32, error) {
var idsToRead uint16
var idsTmp []uint32
var err error
var rdr *metadata.Reader
var rdr metadata.Reader
// We can *maybe* have a slight speed increase by manually decoding instead of using reflection via binary.Read
for i := blocksRead; i < int(blocksRead)+blocksToRead; i++ {
err = binary.Read(toreader.NewReader(r.r, int64(r.Superblock.IdTableStart)+int64(8*i)), binary.LittleEndian, &offset)
if err != nil {
return 0, err
}
idsToRead = r.Superblock.IdCount - uint16(len(r.idTable))
if idsToRead > 2048 {
idsToRead = 2048
}
idsToRead = min(r.Superblock.IdCount-uint16(len(r.idTable)), 2048)
idsTmp = make([]uint32, idsToRead)
rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
err = binary.Read(rdr, binary.LittleEndian, &idsTmp)
err = binary.Read(&rdr, binary.LittleEndian, &idsTmp)
rdr.Close()
if err != nil {
return 0, err
@@ -157,7 +160,7 @@ func (r *Reader) inodeRef(i uint32) (uint64, error) {
// Populate the export table as needed
var blockNum uint32
if i != 0 { // If i == 0, we go negatives causing issues with uint32s
blockNum = uint32(math.Ceil(float64(i)/1024)) - 1
blockNum = uint32(math.Ceil(float64(i+1)/1024)) - 1
} else {
blockNum = 0
}
@@ -168,19 +171,17 @@ func (r *Reader) inodeRef(i uint32) (uint64, error) {
var refsToRead uint32
var refsTmp []uint64
var err error
var rdr *metadata.Reader
var rdr metadata.Reader
// We can *maybe* have a slight speed increase by manually decoding instead of using reflection via binary.Read
for i := blocksRead; i < int(blocksRead)+blocksToRead; i++ {
err = binary.Read(toreader.NewReader(r.r, int64(r.Superblock.ExportTableStart)+int64(8*i)), binary.LittleEndian, &offset)
if err != nil {
return 0, err
}
refsToRead = r.Superblock.InodeCount - uint32(len(r.exportTable))
if refsToRead > 1024 {
refsToRead = 1024
}
refsToRead = min(r.Superblock.InodeCount-uint32(len(r.exportTable)), 1024)
refsTmp = make([]uint64, refsToRead)
rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
err = binary.Read(rdr, binary.LittleEndian, &refsTmp)
err = binary.Read(&rdr, binary.LittleEndian, &refsTmp)
rdr.Close()
if err != nil {
return 0, err
@@ -190,7 +191,7 @@ func (r *Reader) inodeRef(i uint32) (uint64, error) {
return r.exportTable[i], nil
}
func (r *Reader) Inode(i uint32) (inode.Inode, error) {
func (r Reader) Inode(i uint32) (inode.Inode, error) {
ref, err := r.inodeRef(i)
if err != nil {
return inode.Inode{}, err
+22 -9
View File
@@ -1,4 +1,4 @@
package squashfslow_test
package squashfslow
import (
"fmt"
@@ -8,13 +8,11 @@ import (
"os/exec"
"path/filepath"
"testing"
squashfslow "github.com/CalebQ42/squashfs/low"
)
const (
squashfsURL = "https://darkstorm.tech/files/LinuxPATest.sfs"
squashfsName = "LinuxPATest.sfs"
squashfsName = "airootfs.sfs"
)
func preTest(dir string) (fil *os.File, err error) {
@@ -50,6 +48,21 @@ func preTest(dir string) (fil *os.File, err error) {
return
}
func TestMisc(t *testing.T) {
tmpDir := "../testing"
fil, err := preTest(tmpDir)
if err != nil {
t.Fatal(err)
}
defer fil.Close()
rdr, err := NewReader(fil)
if err != nil {
t.Fatal(err)
}
t.Log(rdr.Superblock.FragCount)
t.Fatal(rdr.fragEntry(1233))
}
func TestReader(t *testing.T) {
tmpDir := "../testing"
fil, err := preTest(tmpDir)
@@ -57,7 +70,7 @@ func TestReader(t *testing.T) {
t.Fatal(err)
}
defer fil.Close()
rdr, err := squashfslow.NewReader(fil)
rdr, err := NewReader(fil)
if err != nil {
t.Fatal(err)
}
@@ -79,7 +92,7 @@ func TestSingleFile(t *testing.T) {
t.Fatal(err)
}
defer fil.Close()
rdr, err := squashfslow.NewReader(fil)
rdr, err := NewReader(fil)
if err != nil {
t.Fatal(err)
}
@@ -96,7 +109,7 @@ func TestSingleFile(t *testing.T) {
}
}
func extractToDir(rdr *squashfslow.Reader, b *squashfslow.FileBase, folder string) error {
func extractToDir(rdr Reader, b FileBase, folder string) error {
path := filepath.Join(folder, b.Name)
if b.IsDir() {
d, err := b.ToDir(rdr)
@@ -107,13 +120,13 @@ func extractToDir(rdr *squashfslow.Reader, b *squashfslow.FileBase, folder strin
if err != nil {
return err
}
var nestBast squashfslow.FileBase
var nestBast FileBase
for _, e := range d.Entries {
nestBast, err = rdr.BaseFromEntry(e)
if err != nil {
return err
}
err = extractToDir(rdr, &nestBast, path)
err = extractToDir(rdr, nestBast, path)
if err != nil {
return err
}