Finished?
Everything seems to extract fine (though more testing is needed)
This commit is contained in:
@@ -60,3 +60,8 @@ func (r *Reader) Read(b []byte) (int, error) {
|
||||
}
|
||||
return curRead, nil
|
||||
}
|
||||
|
||||
func (r *Reader) Close() error {
|
||||
r.dat = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
+53
-14
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/CalebQ42/squashfs/internal/decompress"
|
||||
"github.com/CalebQ42/squashfs/internal/metadata"
|
||||
"github.com/CalebQ42/squashfs/internal/toreader"
|
||||
"github.com/CalebQ42/squashfs/squashfs/data"
|
||||
@@ -17,26 +16,35 @@ type Base struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (r *Reader) baseFromInode(i *inode.Inode, name string) *Base {
|
||||
func (r *Reader) BaseFromInode(i *inode.Inode, name string) *Base {
|
||||
return &Base{Inode: i, Name: name}
|
||||
}
|
||||
|
||||
func (r *Reader) baseFromEntry(e directory.Entry) (*Base, error) {
|
||||
rdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.sup.InodeTableStart)+int64(e.BlockStart)), r.d)
|
||||
func (r *Reader) BaseFromEntry(e directory.Entry) (*Base, 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))
|
||||
in, err := inode.Read(rdr, r.sup.BlockSize)
|
||||
in, err := inode.Read(rdr, r.Superblock.BlockSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Base{Inode: in, Name: e.Name}, nil
|
||||
}
|
||||
|
||||
func (r *Reader) BaseFromRef(ref uint64, name string) (*Base, error) {
|
||||
in, err := r.inodeFromRef(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Base{Inode: in, Name: name}, nil
|
||||
}
|
||||
|
||||
func (b *Base) Uid(r *Reader) (uint32, error) {
|
||||
return r.id(b.Inode.UidInd)
|
||||
return r.Id(b.Inode.UidInd)
|
||||
}
|
||||
|
||||
func (b *Base) Gid(r *Reader) (uint32, error) {
|
||||
return r.id(b.Inode.GidInd)
|
||||
return r.Id(b.Inode.GidInd)
|
||||
}
|
||||
|
||||
func (b *Base) IsDir() bool {
|
||||
@@ -44,7 +52,35 @@ func (b *Base) IsDir() bool {
|
||||
}
|
||||
|
||||
func (b *Base) ToDir(r *Reader) (*Directory, error) {
|
||||
return r.directoryFromInode(b.Inode, b.Name)
|
||||
var blockStart uint32
|
||||
var size uint32
|
||||
var offset uint16
|
||||
switch b.Inode.Type {
|
||||
case inode.Dir:
|
||||
blockStart = b.Inode.Data.(inode.Directory).BlockStart
|
||||
size = uint32(b.Inode.Data.(inode.Directory).Size)
|
||||
offset = b.Inode.Data.(inode.Directory).Offset
|
||||
case inode.EDir:
|
||||
blockStart = b.Inode.Data.(inode.EDirectory).BlockStart
|
||||
size = b.Inode.Data.(inode.EDirectory).Size
|
||||
offset = b.Inode.Data.(inode.EDirectory).Offset
|
||||
default:
|
||||
return nil, errors.New("not a directory")
|
||||
}
|
||||
dirRdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.Superblock.DirTableStart)+int64(blockStart)), r.d)
|
||||
defer dirRdr.Close()
|
||||
_, err := dirRdr.Read(make([]byte, offset))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries, err := directory.ReadDirectory(dirRdr, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Directory{
|
||||
Base: *b,
|
||||
Entries: entries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *Base) IsRegular() bool {
|
||||
@@ -58,36 +94,39 @@ func (b *Base) GetRegFileReaders(r *Reader) (*data.Reader, *data.FullReader, err
|
||||
var blockStart uint64
|
||||
var fragIndex uint32
|
||||
var fragOffset uint32
|
||||
var fragSize uint64
|
||||
var sizes []uint32
|
||||
if b.Inode.Type == inode.Fil {
|
||||
blockStart = uint64(b.Inode.Data.(inode.File).BlockStart)
|
||||
fragIndex = b.Inode.Data.(inode.File).FragInd
|
||||
fragOffset = b.Inode.Data.(inode.File).FragOffset
|
||||
sizes = b.Inode.Data.(inode.File).BlockSizes
|
||||
fragSize = uint64(b.Inode.Data.(inode.File).Size % r.Superblock.BlockSize)
|
||||
} else {
|
||||
blockStart = b.Inode.Data.(inode.EFile).BlockStart
|
||||
fragIndex = b.Inode.Data.(inode.EFile).FragInd
|
||||
fragOffset = b.Inode.Data.(inode.EFile).FragOffset
|
||||
sizes = b.Inode.Data.(inode.EFile).BlockSizes
|
||||
fragSize = b.Inode.Data.(inode.EFile).Size % uint64(r.Superblock.BlockSize)
|
||||
}
|
||||
frag := func(rdr io.ReaderAt, d decompress.Decompressor) (*data.Reader, error) {
|
||||
frag := func() (io.Reader, error) {
|
||||
ent, err := r.fragEntry(fragIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
frag := data.NewReader(toreader.NewReader(r.r, int64(ent.start)), r.d, []uint32{ent.size})
|
||||
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 frag, nil
|
||||
return io.LimitReader(frag, int64(fragSize)), nil
|
||||
}
|
||||
outRdr := data.NewReader(toreader.NewReader(r.r, int64(blockStart)), r.d, sizes)
|
||||
outRdr := data.NewReader(toreader.NewReader(r.r, int64(blockStart)), r.d, sizes, fragSize, r.Superblock.BlockSize)
|
||||
if fragIndex != 0xffffffff {
|
||||
f, err := frag(r.r, r.d)
|
||||
f, err := frag()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
outRdr.AddFrag(f)
|
||||
}
|
||||
outFull := data.NewFullReader(r.r, int64(blockStart), r.d, sizes)
|
||||
outFull := data.NewFullReader(r.r, int64(blockStart), r.d, sizes, fragSize, r.Superblock.BlockSize)
|
||||
if fragIndex != 0xffffffff {
|
||||
outFull.AddFrag(frag)
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"github.com/CalebQ42/squashfs/internal/decompress"
|
||||
"github.com/CalebQ42/squashfs/internal/toreader"
|
||||
)
|
||||
|
||||
type FragReaderConstructor func(io.ReaderAt, decompress.Decompressor) (*Reader, error)
|
||||
type FragReaderConstructor func() (io.Reader, error)
|
||||
|
||||
type FullReader struct {
|
||||
r io.ReaderAt
|
||||
@@ -19,16 +20,20 @@ type FullReader struct {
|
||||
retPool *sync.Pool
|
||||
sizes []uint32
|
||||
initialOffset int64
|
||||
finalBlockSize uint64
|
||||
blockSize uint32
|
||||
goroutineLimit uint16
|
||||
}
|
||||
|
||||
func NewFullReader(r io.ReaderAt, initialOffset int64, d decompress.Decompressor, sizes []uint32) *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,
|
||||
initialOffset: initialOffset,
|
||||
goroutineLimit: 10,
|
||||
finalBlockSize: finalBlockSize,
|
||||
blockSize: blockSize,
|
||||
retPool: &sync.Pool{
|
||||
New: func() any {
|
||||
return &retValue{}
|
||||
@@ -55,6 +60,16 @@ func (r *FullReader) process(index uint64, fileOffset uint64, retChan chan *retV
|
||||
ret := r.retPool.Get().(*retValue)
|
||||
ret.index = index
|
||||
realSize := r.sizes[index] &^ (1 << 24)
|
||||
if realSize == 0 {
|
||||
if index == uint64(len(r.sizes))-1 && r.frag == nil {
|
||||
ret.data = make([]byte, r.finalBlockSize)
|
||||
} else {
|
||||
ret.data = make([]byte, r.blockSize)
|
||||
}
|
||||
ret.err = nil
|
||||
retChan <- ret
|
||||
return
|
||||
}
|
||||
ret.data = make([]byte, realSize)
|
||||
ret.err = binary.Read(toreader.NewReader(r.r, int64(r.initialOffset)+int64(fileOffset)), binary.LittleEndian, &ret.data)
|
||||
if r.sizes[index] == realSize {
|
||||
@@ -71,7 +86,7 @@ func (r *FullReader) WriteTo(w io.Writer) (int64, error) {
|
||||
cache := make(map[uint64]*retValue)
|
||||
var errCache []error
|
||||
retChan := make(chan *retValue, r.goroutineLimit)
|
||||
for i := uint64(0); i < uint64(len(r.sizes))/uint64(r.goroutineLimit); i++ {
|
||||
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
|
||||
@@ -139,12 +154,17 @@ func (r *FullReader) WriteTo(w io.Writer) (int64, error) {
|
||||
}
|
||||
}
|
||||
if r.frag != nil {
|
||||
rdr, err := r.frag(r.r, r.d)
|
||||
rdr, err := r.frag()
|
||||
if err != nil {
|
||||
return wrote, err
|
||||
}
|
||||
wr, err := io.Copy(w, rdr)
|
||||
wrote += wr
|
||||
if l, ok := rdr.(*io.LimitedReader); ok {
|
||||
if cl, ok := l.R.(io.Closer); ok {
|
||||
cl.Close()
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return wrote, err
|
||||
}
|
||||
|
||||
+37
-21
@@ -8,24 +8,28 @@ import (
|
||||
)
|
||||
|
||||
type Reader struct {
|
||||
r io.Reader
|
||||
d decompress.Decompressor
|
||||
frag *Reader
|
||||
sizes []uint32
|
||||
dat []byte
|
||||
curOffset uint16
|
||||
curIndex uint64
|
||||
r io.Reader
|
||||
d decompress.Decompressor
|
||||
frag io.Reader
|
||||
sizes []uint32
|
||||
dat []byte
|
||||
curOffset int
|
||||
curIndex uint64
|
||||
finalBlockSize uint64
|
||||
blockSize uint32
|
||||
}
|
||||
|
||||
func NewReader(r io.Reader, d decompress.Decompressor, sizes []uint32) *Reader {
|
||||
func NewReader(r io.Reader, d decompress.Decompressor, sizes []uint32, finalBlockSize uint64, blockSize uint32) *Reader {
|
||||
return &Reader{
|
||||
r: r,
|
||||
d: d,
|
||||
sizes: sizes,
|
||||
r: r,
|
||||
d: d,
|
||||
sizes: sizes,
|
||||
finalBlockSize: finalBlockSize,
|
||||
blockSize: blockSize,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reader) AddFrag(fragRdr *Reader) {
|
||||
func (r *Reader) AddFrag(fragRdr io.Reader) {
|
||||
r.frag = fragRdr
|
||||
}
|
||||
|
||||
@@ -33,13 +37,21 @@ func (r *Reader) advance() error {
|
||||
r.curOffset = 0
|
||||
defer func() { r.curIndex++ }()
|
||||
var err error
|
||||
if r.curIndex == uint64(len(r.sizes))-1 && r.frag != nil {
|
||||
if r.curIndex == uint64(len(r.sizes)) && r.frag != nil {
|
||||
r.dat, err = io.ReadAll(r.frag)
|
||||
return err
|
||||
} else if r.curIndex >= uint64(len(r.sizes))-1 {
|
||||
} else if r.curIndex >= uint64(len(r.sizes)) {
|
||||
return io.EOF
|
||||
}
|
||||
realSize := r.sizes[r.curIndex] &^ 0x8000
|
||||
realSize := r.sizes[r.curIndex] &^ (1 << 24)
|
||||
if realSize == 0 {
|
||||
if r.curIndex == uint64(len(r.sizes))-1 && r.frag == nil {
|
||||
r.dat = make([]byte, r.finalBlockSize)
|
||||
} else {
|
||||
r.dat = make([]byte, r.blockSize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
r.dat = make([]byte, realSize)
|
||||
err = binary.Read(r.r, binary.LittleEndian, &r.dat)
|
||||
if err != nil {
|
||||
@@ -56,17 +68,17 @@ func (r *Reader) Read(b []byte) (int, error) {
|
||||
curRead := 0
|
||||
var toRead int
|
||||
for curRead < len(b) {
|
||||
if r.curOffset >= uint16(len(r.dat)) {
|
||||
if r.curOffset >= len(r.dat) {
|
||||
if err := r.advance(); err != nil {
|
||||
return curRead, err
|
||||
}
|
||||
}
|
||||
toRead = len(b) - curRead
|
||||
if toRead > len(r.dat)-int(r.curOffset) {
|
||||
toRead = len(r.dat) - int(r.curOffset)
|
||||
if toRead > len(r.dat)-r.curOffset {
|
||||
toRead = len(r.dat) - r.curOffset
|
||||
}
|
||||
copy(b[curRead:], r.dat[r.curOffset:int(r.curOffset)+toRead])
|
||||
r.curOffset += uint16(toRead)
|
||||
toRead = copy(b[curRead:], r.dat[r.curOffset:r.curOffset+toRead])
|
||||
r.curOffset += toRead
|
||||
curRead += toRead
|
||||
}
|
||||
return curRead, nil
|
||||
@@ -74,7 +86,11 @@ func (r *Reader) Read(b []byte) (int, error) {
|
||||
|
||||
func (r *Reader) Close() error {
|
||||
if r.frag != nil {
|
||||
return r.frag.Close()
|
||||
if l, ok := r.frag.(*io.LimitedReader); ok {
|
||||
if cl, ok := l.R.(io.Closer); ok {
|
||||
cl.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
r.dat = nil
|
||||
return nil
|
||||
|
||||
+12
-13
@@ -2,6 +2,7 @@ package squashfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
@@ -18,7 +19,12 @@ type Directory struct {
|
||||
Entries []directory.Entry
|
||||
}
|
||||
|
||||
func (r *Reader) directoryFromInode(i *inode.Inode, name string) (*Directory, error) {
|
||||
func (r *Reader) directoryFromRef(ref uint64, name string) (*Directory, error) {
|
||||
i, err := r.inodeFromRef(ref)
|
||||
if err != nil {
|
||||
fmt.Println("yo")
|
||||
return nil, err
|
||||
}
|
||||
var blockStart uint32
|
||||
var size uint32
|
||||
var offset uint16
|
||||
@@ -34,8 +40,9 @@ func (r *Reader) directoryFromInode(i *inode.Inode, name string) (*Directory, er
|
||||
default:
|
||||
return nil, errors.New("not a directory")
|
||||
}
|
||||
dirRdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.sup.DirTableStart)+int64(blockStart)), r.d)
|
||||
_, err := dirRdr.Read(make([]byte, offset))
|
||||
dirRdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.Superblock.DirTableStart)+int64(blockStart)), r.d)
|
||||
defer dirRdr.Close()
|
||||
_, err = dirRdr.Read(make([]byte, offset))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -44,19 +51,11 @@ func (r *Reader) directoryFromInode(i *inode.Inode, name string) (*Directory, er
|
||||
return nil, err
|
||||
}
|
||||
return &Directory{
|
||||
Base: *r.baseFromInode(i, name),
|
||||
Base: *r.BaseFromInode(i, name),
|
||||
Entries: entries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Reader) directoryFromRef(ref uint64, name string) (*Directory, error) {
|
||||
in, err := r.inodeFromRef(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.directoryFromInode(in, name)
|
||||
}
|
||||
|
||||
func (d *Directory) Open(r *Reader, path string) (*Base, error) {
|
||||
path = filepath.Clean(path)
|
||||
if path == "." || path == "" {
|
||||
@@ -69,7 +68,7 @@ func (d *Directory) Open(r *Reader, path string) (*Base, error) {
|
||||
if !found {
|
||||
return nil, fs.ErrNotExist
|
||||
}
|
||||
b, err := r.baseFromEntry(d.Entries[i])
|
||||
b, err := r.BaseFromEntry(d.Entries[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package squashfs
|
||||
|
||||
type fragEntry struct {
|
||||
start uint64
|
||||
size uint32
|
||||
Start uint64
|
||||
Size uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
+3
-2
@@ -7,11 +7,12 @@ import (
|
||||
)
|
||||
|
||||
func (r *Reader) inodeFromRef(ref uint64) (*inode.Inode, error) {
|
||||
offset, meta := (ref>>16)+r.sup.InodeTableStart, ref&0xFFFF
|
||||
offset, meta := (ref>>16)+r.Superblock.InodeTableStart, ref&0xFFFF
|
||||
rdr := metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
|
||||
defer rdr.Close()
|
||||
_, err := rdr.Read(make([]byte, meta))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return inode.Read(rdr, r.sup.BlockSize)
|
||||
return inode.Read(rdr, r.Superblock.BlockSize)
|
||||
}
|
||||
|
||||
+68
-34
@@ -8,7 +8,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/CalebQ42/squashfs/internal/decompress"
|
||||
"github.com/CalebQ42/squashfs/internal/metadata"
|
||||
"github.com/CalebQ42/squashfs/internal/toreader"
|
||||
"github.com/CalebQ42/squashfs/squashfs/inode"
|
||||
)
|
||||
|
||||
// The types of compression supported by squashfs
|
||||
@@ -31,30 +33,30 @@ var (
|
||||
type Reader struct {
|
||||
r io.ReaderAt
|
||||
d decompress.Decompressor
|
||||
root *Directory
|
||||
Root *Directory
|
||||
fragTable []fragEntry
|
||||
idTable []uint32
|
||||
exportTable []uint64
|
||||
sup superblock
|
||||
Superblock superblock
|
||||
}
|
||||
|
||||
func NewReader(r io.ReaderAt) (rdr *Reader, err error) {
|
||||
rdr = new(Reader)
|
||||
rdr.r = r
|
||||
err = binary.Read(toreader.NewReader(r, 0), binary.LittleEndian, &rdr.sup)
|
||||
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)
|
||||
}
|
||||
if !rdr.sup.checkMagic() {
|
||||
if !rdr.Superblock.ValidMagic() {
|
||||
return nil, ErrorMagic
|
||||
}
|
||||
if !rdr.sup.checkBlockLog() {
|
||||
if !rdr.Superblock.ValidBlockLog() {
|
||||
return nil, ErrorLog
|
||||
}
|
||||
if !rdr.sup.checkVersion() {
|
||||
if !rdr.Superblock.ValidVersion() {
|
||||
return nil, ErrorVersion
|
||||
}
|
||||
switch rdr.sup.CompType {
|
||||
switch rdr.Superblock.CompType {
|
||||
case ZlibCompression:
|
||||
rdr.d = decompress.Zlib{}
|
||||
case LZMACompression:
|
||||
@@ -70,7 +72,7 @@ func NewReader(r io.ReaderAt) (rdr *Reader, err error) {
|
||||
default:
|
||||
return nil, errors.New("invalid compression type. possible corrupted archive")
|
||||
}
|
||||
rdr.root, err = rdr.directoryFromRef(rdr.sup.RootInodeRef, "")
|
||||
rdr.Root, err = rdr.directoryFromRef(rdr.Superblock.RootInodeRef, "")
|
||||
if err != nil {
|
||||
return nil, errors.Join(errors.New("failed to read root directory"), err)
|
||||
}
|
||||
@@ -79,36 +81,44 @@ func NewReader(r io.ReaderAt) (rdr *Reader, err error) {
|
||||
|
||||
// Returns the last time the archive was modified.
|
||||
func (r *Reader) ModTime() time.Time {
|
||||
return time.Unix(int64(r.sup.ModTime), 0)
|
||||
return time.Unix(int64(r.Superblock.ModTime), 0)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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) {
|
||||
if len(r.idTable) > int(i) {
|
||||
return r.idTable[i], nil
|
||||
} else if i >= r.sup.IdCount {
|
||||
} else if i >= r.Superblock.IdCount {
|
||||
return 0, errors.New("id out of bounds")
|
||||
}
|
||||
// Populate the id table as needed
|
||||
blockNum := uint16(math.Ceil(float64(i) / 2048))
|
||||
var blockNum uint32
|
||||
if i != 0 { // If i == 0, we go negatives causing issues with uint32s
|
||||
blockNum = uint32(math.Ceil(float64(i)/2048)) - 1
|
||||
} else {
|
||||
blockNum = 0
|
||||
}
|
||||
blocksRead := len(r.idTable) / 2048
|
||||
blocksToRead := int(blockNum) - blocksRead
|
||||
blocksToRead := int(blockNum) - blocksRead + 1
|
||||
|
||||
var offset uint64
|
||||
var idsToRead uint16
|
||||
var idsTmp []uint32
|
||||
var err error
|
||||
for i := blocksRead; i < int(blockNum)+blocksToRead; i++ {
|
||||
err = binary.Read(toreader.NewReader(r.r, int64(r.sup.IdTableStart)+int64(8*i)), binary.LittleEndian, &offset)
|
||||
var rdr *metadata.Reader
|
||||
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.sup.IdCount - uint16(len(r.idTable))
|
||||
idsToRead = r.Superblock.IdCount - uint16(len(r.idTable))
|
||||
if idsToRead > 2048 {
|
||||
idsToRead = 2048
|
||||
}
|
||||
idsTmp = make([]uint32, idsToRead)
|
||||
err = binary.Read(toreader.NewReader(r.r, int64(offset)), binary.LittleEndian, &idsTmp)
|
||||
rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
|
||||
err = binary.Read(rdr, binary.LittleEndian, &idsTmp)
|
||||
rdr.Close()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -121,29 +131,37 @@ func (r *Reader) id(i uint16) (uint32, error) {
|
||||
func (r *Reader) fragEntry(i uint32) (fragEntry, error) {
|
||||
if len(r.fragTable) > int(i) {
|
||||
return r.fragTable[i], nil
|
||||
} else if i >= r.sup.FragCount {
|
||||
} else if i >= r.Superblock.FragCount {
|
||||
return fragEntry{}, errors.New("fragment out of bounds")
|
||||
}
|
||||
// Populate the fragment table as needed
|
||||
blockNum := uint32(math.Ceil(float64(i) / 512))
|
||||
var blockNum uint32
|
||||
if i != 0 { // If i == 0, we go negatives causing issues with uint32s
|
||||
blockNum = uint32(math.Ceil(float64(i)/512)) - 1
|
||||
} else {
|
||||
blockNum = 0
|
||||
}
|
||||
blocksRead := len(r.fragTable) / 512
|
||||
blocksToRead := int(blockNum) - blocksRead
|
||||
blocksToRead := int(blockNum) - blocksRead + 1
|
||||
|
||||
var offset uint64
|
||||
var fragsToRead uint32
|
||||
var fragsTmp []fragEntry
|
||||
var err error
|
||||
for i := blocksRead; i < int(blockNum)+blocksToRead; i++ {
|
||||
err = binary.Read(toreader.NewReader(r.r, int64(r.sup.FragTableStart)+int64(8*i)), binary.LittleEndian, &offset)
|
||||
var rdr *metadata.Reader
|
||||
for i := blocksRead; i < int(blocksRead)+blocksToRead; i++ {
|
||||
err = binary.Read(toreader.NewReader(r.r, int64(r.Superblock.FragTableStart)+int64(8*i)), binary.LittleEndian, &offset)
|
||||
if err != nil {
|
||||
return fragEntry{}, err
|
||||
}
|
||||
fragsToRead = r.sup.FragCount - uint32(len(r.fragTable))
|
||||
fragsToRead = r.Superblock.FragCount - uint32(len(r.fragTable))
|
||||
if fragsToRead > 512 {
|
||||
fragsToRead = 512
|
||||
}
|
||||
fragsTmp = make([]fragEntry, fragsToRead)
|
||||
err = binary.Read(toreader.NewReader(r.r, int64(offset)), binary.LittleEndian, &fragsTmp)
|
||||
rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
|
||||
err = binary.Read(rdr, binary.LittleEndian, &fragsTmp)
|
||||
rdr.Close()
|
||||
if err != nil {
|
||||
return fragEntry{}, err
|
||||
}
|
||||
@@ -154,34 +172,42 @@ 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) {
|
||||
if !r.sup.exportable() {
|
||||
if !r.Superblock.Exportable() {
|
||||
return 0, ErrorNotExportable
|
||||
}
|
||||
if len(r.exportTable) > int(i) {
|
||||
return r.exportTable[i], nil
|
||||
} else if i >= r.sup.InodeCount {
|
||||
} else if i >= r.Superblock.InodeCount {
|
||||
return 0, errors.New("inode out of bounds")
|
||||
}
|
||||
// Populate the export table as neede
|
||||
blockNum := uint32(math.Ceil(float64(i) / 1024))
|
||||
// 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
|
||||
} else {
|
||||
blockNum = 0
|
||||
}
|
||||
blocksRead := len(r.exportTable) / 1024
|
||||
blocksToRead := int(blockNum) - blocksRead
|
||||
blocksToRead := int(blockNum) - blocksRead + 1
|
||||
|
||||
var offset uint64
|
||||
var refsToRead uint32
|
||||
var refsTmp []uint64
|
||||
var err error
|
||||
for i := blocksRead; i < int(blockNum)+blocksToRead; i++ {
|
||||
err = binary.Read(toreader.NewReader(r.r, int64(r.sup.ExportTableStart)+int64(8*i)), binary.LittleEndian, &offset)
|
||||
var rdr *metadata.Reader
|
||||
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.sup.InodeCount - uint32(len(r.exportTable))
|
||||
refsToRead = r.Superblock.InodeCount - uint32(len(r.exportTable))
|
||||
if refsToRead > 1024 {
|
||||
refsToRead = 1024
|
||||
}
|
||||
refsTmp = make([]uint64, refsToRead)
|
||||
err = binary.Read(toreader.NewReader(r.r, int64(offset)), binary.LittleEndian, &refsTmp)
|
||||
rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
|
||||
err = binary.Read(rdr, binary.LittleEndian, &refsTmp)
|
||||
rdr.Close()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -189,3 +215,11 @@ func (r *Reader) inodeRef(i uint32) (uint64, error) {
|
||||
}
|
||||
return r.exportTable[i], nil
|
||||
}
|
||||
|
||||
func (r *Reader) Inode(i uint32) (*inode.Inode, error) {
|
||||
ref, err := r.inodeRef(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.inodeFromRef(ref)
|
||||
}
|
||||
|
||||
+59
-14
@@ -1,21 +1,18 @@
|
||||
package squashfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/CalebQ42/squashfs/squashfs/inode"
|
||||
)
|
||||
|
||||
const (
|
||||
squashfsURL = "https://darkstorm.tech/files/LinuxPATest.sfs"
|
||||
squashfsName = "LinuxPATest.sfs"
|
||||
|
||||
// filePath = "PortableApps/Notepad++Portable/App/DefaultData/Config/contextMenu.xml"
|
||||
)
|
||||
|
||||
func preTest(dir string) (fil *os.File, err error) {
|
||||
@@ -62,26 +59,74 @@ func TestReader(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = checkDir(rdr, rdr.root)
|
||||
path := filepath.Join(tmpDir, "extractTest")
|
||||
os.RemoveAll(path)
|
||||
os.MkdirAll(path, 0777)
|
||||
err = extractToDir(rdr, &rdr.Root.Base, path)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
func checkDir(rdr *Reader, d *Directory) error {
|
||||
for _, e := range d.Entries {
|
||||
if e.InodeType == inode.Dir {
|
||||
b, err := d.Open(rdr, e.Name)
|
||||
var singleFile = "PortableApps/CPU-X/CPU-X-v4.2.0-x86_64.AppImage"
|
||||
|
||||
func TestSingleFile(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)
|
||||
}
|
||||
path := filepath.Join(tmpDir, "extractTest")
|
||||
os.RemoveAll(path)
|
||||
os.MkdirAll(path, 0777)
|
||||
b, err := rdr.Root.Open(rdr, singleFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = extractToDir(rdr, b, path)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
func extractToDir(rdr *Reader, b *Base, folder string) error {
|
||||
path := filepath.Join(folder, b.Name)
|
||||
if b.IsDir() {
|
||||
d, err := b.ToDir(rdr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(path, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var nestBast *Base
|
||||
for _, e := range d.Entries {
|
||||
nestBast, err = rdr.BaseFromEntry(e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d, err := b.ToDir(rdr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = checkDir(rdr, d)
|
||||
err = extractToDir(rdr, nestBast, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if b.IsRegular() {
|
||||
_, full, err := b.GetRegFileReaders(rdr)
|
||||
if err != nil {
|
||||
fmt.Println("yo", path)
|
||||
return err
|
||||
}
|
||||
fil, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = full.WriteTo(fil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Successfully extracted file:", b.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+14
-14
@@ -24,57 +24,57 @@ type superblock struct {
|
||||
ExportTableStart uint64
|
||||
}
|
||||
|
||||
func (s superblock) checkMagic() bool {
|
||||
func (s superblock) ValidMagic() bool {
|
||||
return s.Magic == 0x73717368
|
||||
}
|
||||
|
||||
func (s superblock) checkBlockLog() bool {
|
||||
func (s superblock) ValidBlockLog() bool {
|
||||
return s.BlockLog == uint16(math.Log2(float64(s.BlockSize)))
|
||||
}
|
||||
|
||||
func (s superblock) checkVersion() bool {
|
||||
func (s superblock) ValidVersion() bool {
|
||||
return s.VerMaj == 4 && s.VerMin == 0
|
||||
}
|
||||
|
||||
func (s superblock) uncompressedInodes() bool {
|
||||
func (s superblock) UncompressedInodes() bool {
|
||||
return s.Flags&0x1 == 0x1
|
||||
}
|
||||
|
||||
func (s superblock) uncompressedData() bool {
|
||||
func (s superblock) UncompressedData() bool {
|
||||
return s.Flags&0x2 == 0x2
|
||||
}
|
||||
func (s superblock) uncompressedFragments() bool {
|
||||
func (s superblock) UncompressedFragments() bool {
|
||||
return s.Flags&0x8 == 0x8
|
||||
}
|
||||
|
||||
func (s superblock) noFragments() bool {
|
||||
func (s superblock) NoFragments() bool {
|
||||
return s.Flags&0x10 == 0x10
|
||||
}
|
||||
|
||||
func (s superblock) alwaysFragment() bool {
|
||||
func (s superblock) AlwaysFragment() bool {
|
||||
return s.Flags&0x20 == 0x20
|
||||
}
|
||||
|
||||
func (s superblock) duplicates() bool {
|
||||
func (s superblock) Duplicates() bool {
|
||||
return s.Flags&0x40 == 0x40
|
||||
}
|
||||
|
||||
func (s superblock) exportable() bool {
|
||||
func (s superblock) Exportable() bool {
|
||||
return s.Flags&0x80 == 0x80
|
||||
}
|
||||
|
||||
func (s superblock) uncompressedXattrs() bool {
|
||||
func (s superblock) UncompressedXattrs() bool {
|
||||
return s.Flags&0x100 == 0x100
|
||||
}
|
||||
|
||||
func (s superblock) noXattrs() bool {
|
||||
func (s superblock) NoXattrs() bool {
|
||||
return s.Flags&0x200 == 0x200
|
||||
}
|
||||
|
||||
func (s superblock) compressionOptions() bool {
|
||||
func (s superblock) CompressionOptions() bool {
|
||||
return s.Flags&0x400 == 0x400
|
||||
}
|
||||
|
||||
func (s superblock) uncompressedIDs() bool {
|
||||
func (s superblock) UncompressedIDs() bool {
|
||||
return s.Flags&0x800 == 0x800
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user