Merge branch 'main' into upstream-fix-out-of-range-panic
This commit is contained in:
+107
-21
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user