Merge pull request #39 from CalebQ42/perfExp

Performance
This commit is contained in:
Caleb Gardner
2025-04-10 11:26:24 -05:00
committed by GitHub
19 changed files with 271 additions and 169 deletions
+5 -6
View File
@@ -28,15 +28,14 @@ As of `v1.0`, FUSE capabilities has been moved to [a separate library](https://g
## Issues ## Issues
* Significantly slower then `unsquashfs` when nested images * Noticably slower then `unsquashfs` for extraction, especially on larger images.
* This seems to be related to above along with the general optimization of `unsquashfs` and it's compression libraries. * This seems to be related to above along with the general optimization of `unsquashfs` and it's compression libraries.
* Not to mention it's written in C
* Times seem to be largely dependent on file tree size and compression type. * Times seem to be largely dependent on file tree size and compression type.
* My main testing image (~100MB) using Zstd takes about 5x longer. * My main testing image (~100MB) using Zstd takes ~2x longer.
* An Arch Linux airootfs image (~780MB) using XZ compression with LZMA filters takes about 30x longer. * An Arch Linux airootfs image (~780MB) using XZ compression with LZMA filters takes ~28x longer.
* A Tensorflow docker image (~3.3GB) using Zstd takes about 12x longer. * A Tensorflow docker image (~3.3GB) using Zstd takes ~3x longer.
Note: These numbers are using `FastOptions()`. `DefaultOptions()` takes about 2x longer. Note: These numbers are using `FastOptions()`. `DefaultOptions()` takes ~2x longer.
## Recommendations on Usage ## Recommendations on Usage
+36 -32
View File
@@ -19,49 +19,47 @@ import (
// File represents a file inside a squashfs archive. // File represents a file inside a squashfs archive.
type File struct { type File struct {
full *data.FullReader full data.FullReader
rdr *data.Reader rdr data.Reader
parent *FS rdrInit bool
parent FS
r *Reader r *Reader
b squashfslow.FileBase b squashfslow.FileBase
dirsRead int dirsRead int
} }
// Creates a new *File from the given *squashfs.Base // Creates a new *File from the given *squashfs.Base
func (r *Reader) FileFromBase(b squashfslow.FileBase, parent *FS) *File { func (r *Reader) FileFromBase(b squashfslow.FileBase, parent FS) File {
return &File{ return File{
b: b, b: b,
parent: parent, parent: parent,
r: r, r: r,
} }
} }
func (f *File) FS() (*FS, error) { func (f File) FS() (FS, error) {
if !f.IsDir() { if !f.IsDir() {
return nil, errors.New("not a directory") return FS{}, errors.New("not a directory")
} }
d, err := f.b.ToDir(&f.r.Low) d, err := f.b.ToDir(f.r.Low)
if err != nil { if err != nil {
return nil, err return FS{}, err
} }
return &FS{d: d, parent: f.parent, r: f.r}, nil return FS{d: d, parent: &f.parent, r: f.r}, nil
} }
// Closes the underlying readers. // Closes the underlying readers.
// Further calls to Read and WriteTo will re-create the readers. // Further calls to Read and WriteTo will re-create the readers.
// Never returns an error. // Never returns an error.
func (f *File) Close() error { func (f *File) Close() error {
if f.rdr != nil { f.rdr.Close()
return f.rdr.Close() f.full.Close()
}
f.rdr = nil
f.full = nil
return nil return nil
} }
// Returns the file the symlink points to. // Returns the file the symlink points to.
// If the file isn't a symlink, or points to a file outside the archive, returns nil. // If the file isn't a symlink, or points to a file outside the archive, returns nil.
func (f *File) GetSymlinkFile() fs.File { func (f File) GetSymlinkFile() fs.File {
if !f.IsSymlink() { if !f.IsSymlink() {
return nil return nil
} }
@@ -76,21 +74,21 @@ func (f *File) GetSymlinkFile() fs.File {
} }
// Returns whether the file is a directory. // Returns whether the file is a directory.
func (f *File) IsDir() bool { func (f File) IsDir() bool {
return f.b.IsDir() return f.b.IsDir()
} }
// Returns whether the file is a regular file. // Returns whether the file is a regular file.
func (f *File) IsRegular() bool { func (f File) IsRegular() bool {
return f.b.IsRegular() return f.b.IsRegular()
} }
// Returns whether the file is a symlink. // Returns whether the file is a symlink.
func (f *File) IsSymlink() bool { func (f File) IsSymlink() bool {
return f.b.Inode.Type == inode.Sym || f.b.Inode.Type == inode.ESym return f.b.Inode.Type == inode.Sym || f.b.Inode.Type == inode.ESym
} }
func (f *File) Mode() fs.FileMode { func (f File) Mode() fs.FileMode {
return f.b.Inode.Mode() return f.b.Inode.Mode()
} }
@@ -99,7 +97,7 @@ func (f *File) Read(b []byte) (int, error) {
if !f.IsRegular() { if !f.IsRegular() {
return 0, errors.New("file is not a regular file") return 0, errors.New("file is not a regular file")
} }
if f.rdr == nil { if !f.rdrInit {
err := f.initializeReaders() err := f.initializeReaders()
if err != nil { if err != nil {
return 0, err return 0, err
@@ -114,7 +112,7 @@ func (f *File) ReadDir(n int) ([]fs.DirEntry, error) {
if !f.IsDir() { if !f.IsDir() {
return nil, errors.New("file is not a directory") return nil, errors.New("file is not a directory")
} }
d, err := f.b.ToDir(&f.r.Low) d, err := f.b.ToDir(f.r.Low)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -141,7 +139,7 @@ func (f *File) ReadDir(n int) ([]fs.DirEntry, error) {
} }
// Returns the file's fs.FileInfo // Returns the file's fs.FileInfo
func (f *File) Stat() (fs.FileInfo, error) { func (f File) Stat() (fs.FileInfo, error) {
uid, err := f.b.Uid(&f.r.Low) uid, err := f.b.Uid(&f.r.Low)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -154,7 +152,7 @@ func (f *File) Stat() (fs.FileInfo, error) {
} }
// SymlinkPath returns the symlink's target path. Is the File isn't a symlink, returns an empty string. // SymlinkPath returns the symlink's target path. Is the File isn't a symlink, returns an empty string.
func (f *File) SymlinkPath() string { func (f File) SymlinkPath() string {
switch f.b.Inode.Type { switch f.b.Inode.Type {
case inode.Sym: case inode.Sym:
return string(f.b.Inode.Data.(inode.Symlink).Target) return string(f.b.Inode.Data.(inode.Symlink).Target)
@@ -170,7 +168,7 @@ func (f *File) WriteTo(w io.Writer) (int64, error) {
if !f.IsRegular() { if !f.IsRegular() {
return 0, errors.New("file is not a regular file") return 0, errors.New("file is not a regular file")
} }
if f.full == nil { if !f.rdrInit {
err := f.initializeReaders() err := f.initializeReaders()
if err != nil { if err != nil {
return 0, err return 0, err
@@ -181,11 +179,17 @@ func (f *File) WriteTo(w io.Writer) (int64, error) {
func (f *File) initializeReaders() error { func (f *File) initializeReaders() error {
var err error var err error
f.rdr, f.full, err = f.b.GetRegFileReaders(&f.r.Low) f.rdr, f.full, err = f.b.GetRegFileReaders(f.r.Low)
if err == nil {
f.rdrInit = true
} else {
f.rdr.Close()
f.full.Close()
}
return err return err
} }
func (f *File) deviceDevices() (maj uint32, min uint32) { func (f File) deviceDevices() (maj uint32, min uint32) {
var dev uint32 var dev uint32
switch f.b.Inode.Type { switch f.b.Inode.Type {
case inode.Char, inode.Block: case inode.Char, inode.Block:
@@ -196,8 +200,8 @@ func (f *File) deviceDevices() (maj uint32, min uint32) {
return dev >> 8, dev & 0x000FF return dev >> 8, dev & 0x000FF
} }
func (f *File) path() string { func (f File) path() string {
if f.parent == nil { if f.parent.d.Name == "" {
return f.b.Name return f.b.Name
} }
return filepath.Join(f.parent.path(), f.b.Name) return filepath.Join(f.parent.path(), f.b.Name)
@@ -205,13 +209,13 @@ func (f *File) path() string {
// Extract the file to the given folder. If the file is a folder, the folder's contents will be extracted to the folder. // Extract the file to the given folder. If the file is a folder, the folder's contents will be extracted to the folder.
// Uses default extraction options. // Uses default extraction options.
func (f *File) Extract(folder string) error { func (f File) Extract(folder string) error {
return f.ExtractWithOptions(folder, DefaultOptions()) return f.ExtractWithOptions(folder, DefaultOptions())
} }
// Extract the file to the given folder. If the file is a folder, the folder's contents will be extracted to the folder. // Extract the file to the given folder. If the file is a folder, the folder's contents will be extracted to the folder.
// Allows setting various extraction options via ExtractionOptions. // Allows setting various extraction options via ExtractionOptions.
func (f *File) ExtractWithOptions(path string, op *ExtractionOptions) error { func (f File) ExtractWithOptions(path string, op *ExtractionOptions) error {
if op.manager == nil { if op.manager == nil {
op.manager = routinemanager.NewManager(op.SimultaneousFiles) op.manager = routinemanager.NewManager(op.SimultaneousFiles)
if op.LogOutput != nil { if op.LogOutput != nil {
@@ -227,7 +231,7 @@ func (f *File) ExtractWithOptions(path string, op *ExtractionOptions) error {
} }
switch f.b.Inode.Type { switch f.b.Inode.Type {
case inode.Dir, inode.EDir: case inode.Dir, inode.EDir:
d, err := f.b.ToDir(&f.r.Low) d, err := f.b.ToDir(f.r.Low)
if err != nil { if err != nil {
if op.Verbose { if op.Verbose {
log.Println("Failed to create squashfs.Directory for", path) log.Println("Failed to create squashfs.Directory for", path)
+22 -16
View File
@@ -21,11 +21,11 @@ type FS struct {
} }
// Creates a new *FS from the given squashfs.directory // Creates a new *FS from the given squashfs.directory
func (r *Reader) FSFromDirectory(d squashfslow.Directory, parent *FS) *FS { func (r *Reader) FSFromDirectory(d squashfslow.Directory, parent FS) FS {
return &FS{ return FS{
d: d, d: d,
r: r, r: r,
parent: parent, parent: &parent,
} }
} }
@@ -90,7 +90,7 @@ func (f *FS) Glob(pattern string) (out []string, err error) {
} }
// Opens the file at name. Returns a *File as an fs.File. // Opens the file at name. Returns a *File as an fs.File.
func (f *FS) Open(name string) (fs.File, error) { func (f FS) Open(name string) (fs.File, error) {
name = filepath.Clean(name) name = filepath.Clean(name)
if !fs.ValidPath(name) { if !fs.ValidPath(name) {
return nil, &fs.PathError{ return nil, &fs.PathError{
@@ -142,7 +142,7 @@ func (f *FS) Open(name string) (fs.File, error) {
Err: fs.ErrNotExist, Err: fs.ErrNotExist,
} }
} }
d, err := b.ToDir(&f.r.Low) d, err := b.ToDir(f.r.Low)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -151,7 +151,7 @@ func (f *FS) Open(name string) (fs.File, error) {
// Returns all DirEntry's for the directory at name. // Returns all DirEntry's for the directory at name.
// If name is not a directory, returns an error. // If name is not a directory, returns an error.
func (f *FS) ReadDir(name string) ([]fs.DirEntry, error) { func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
name = filepath.Clean(name) name = filepath.Clean(name)
if !fs.ValidPath(name) { if !fs.ValidPath(name) {
return nil, &fs.PathError{ return nil, &fs.PathError{
@@ -171,7 +171,7 @@ func (f *FS) ReadDir(name string) ([]fs.DirEntry, error) {
} }
// Returns the contents of the file at name. // Returns the contents of the file at name.
func (f *FS) ReadFile(name string) (out []byte, err error) { func (f FS) ReadFile(name string) (out []byte, err error) {
name = filepath.Clean(name) name = filepath.Clean(name)
if !fs.ValidPath(name) { if !fs.ValidPath(name) {
return nil, &fs.PathError{ return nil, &fs.PathError{
@@ -194,7 +194,7 @@ func (f *FS) ReadFile(name string) (out []byte, err error) {
} }
// Returns the fs.FileInfo for the file at name. // Returns the fs.FileInfo for the file at name.
func (f *FS) Stat(name string) (fs.FileInfo, error) { func (f FS) Stat(name string) (fs.FileInfo, error) {
name = filepath.Clean(name) name = filepath.Clean(name)
if !fs.ValidPath(name) { if !fs.ValidPath(name) {
return nil, &fs.PathError{ return nil, &fs.PathError{
@@ -214,7 +214,7 @@ func (f *FS) Stat(name string) (fs.FileInfo, error) {
} }
// Returns the FS at dir // Returns the FS at dir
func (f *FS) Sub(dir string) (fs.FS, error) { func (f FS) Sub(dir string) (fs.FS, error) {
dir = filepath.Clean(dir) dir = filepath.Clean(dir)
if !fs.ValidPath(dir) { if !fs.ValidPath(dir) {
return nil, &fs.PathError{ return nil, &fs.PathError{
@@ -242,26 +242,32 @@ func (f *FS) Sub(dir string) (fs.FS, error) {
// Extract the FS to the given folder. If the file is a folder, the folder's contents will be extracted to the folder. // Extract the FS to the given folder. If the file is a folder, the folder's contents will be extracted to the folder.
// Uses default extraction options. // Uses default extraction options.
func (f *FS) Extract(folder string) error { func (f FS) Extract(folder string) error {
return f.File().Extract(folder) return f.File().Extract(folder)
} }
// Extract the FS to the given folder. If the file is a folder, the folder's contents will be extracted to the folder. // Extract the FS to the given folder. If the file is a folder, the folder's contents will be extracted to the folder.
// Allows setting various extraction options via ExtractionOptions. // Allows setting various extraction options via ExtractionOptions.
func (f *FS) ExtractWithOptions(folder string, op *ExtractionOptions) error { func (f FS) ExtractWithOptions(folder string, op *ExtractionOptions) error {
return f.File().ExtractWithOptions(folder, op) return f.File().ExtractWithOptions(folder, op)
} }
// Returns the FS as a *File // Returns the FS as a *File
func (f *FS) File() *File { func (f FS) File() *File {
if f.parent != nil {
return &File{
b: f.d.FileBase,
parent: *f.parent,
r: f.r,
}
}
return &File{ return &File{
b: f.d.FileBase, b: f.d.FileBase,
parent: f.parent, r: f.r,
r: f.r,
} }
} }
func (f *FS) path() string { func (f FS) path() string {
if f.parent == nil { if f.parent == nil {
return f.d.Name return f.d.Name
} }
+18 -3
View File
@@ -3,13 +3,28 @@ package decompress
import ( import (
"bytes" "bytes"
"io" "io"
"sync"
"github.com/pierrec/lz4/v4" "github.com/pierrec/lz4/v4"
) )
type Lz4 struct{} type Lz4 struct {
pool sync.Pool
}
func (l Lz4) Decompress(data []byte) ([]byte, error) { func NewLz4() *Lz4 {
rdr := lz4.NewReader(bytes.NewReader(data)) return &Lz4{
pool: sync.Pool{
New: func() any {
return lz4.NewReader(nil)
},
},
}
}
func (l *Lz4) Decompress(data []byte) ([]byte, error) {
rdr := l.pool.Get().(*lz4.Reader)
defer l.pool.Put(rdr)
rdr.Reset(bytes.NewReader(data))
return io.ReadAll(rdr) return io.ReadAll(rdr)
} }
+19 -3
View File
@@ -3,14 +3,30 @@ package decompress
import ( import (
"bytes" "bytes"
"io" "io"
"sync"
"github.com/therootcompany/xz" "github.com/therootcompany/xz"
) )
type Xz struct{} type Xz struct {
pool sync.Pool
}
func (x Xz) Decompress(data []byte) ([]byte, error) { func NewXz() *Xz {
rdr, err := xz.NewReader(bytes.NewReader(data), 0) return &Xz{
pool: sync.Pool{
New: func() any {
rdr, _ := xz.NewReader(nil, 0)
return rdr
},
},
}
}
func (x *Xz) Decompress(data []byte) ([]byte, error) {
rdr := x.pool.Get().(*xz.Reader)
defer x.pool.Put(rdr)
err := rdr.Reset(bytes.NewReader(data))
if err != nil { if err != nil {
return nil, err return nil, err
} }
+2 -5
View File
@@ -1,19 +1,16 @@
package decompress package decompress
import ( import (
"bytes"
"io"
"github.com/klauspost/compress/zstd" "github.com/klauspost/compress/zstd"
) )
type Zstd struct{} type Zstd struct{}
func (z Zstd) Decompress(data []byte) ([]byte, error) { func (z Zstd) Decompress(data []byte) ([]byte, error) {
rdr, err := zstd.NewReader(bytes.NewReader(data)) rdr, err := zstd.NewReader(nil, zstd.WithDecoderLowmem(true), zstd.WithDecoderConcurrency(1))
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rdr.Close() defer rdr.Close()
return io.ReadAll(rdr) return rdr.DecodeAll(data, nil)
} }
+6 -5
View File
@@ -14,8 +14,8 @@ type Reader struct {
curOffset uint16 curOffset uint16
} }
func NewReader(r io.Reader, d decompress.Decompressor) *Reader { func NewReader(r io.Reader, d decompress.Decompressor) Reader {
return &Reader{ return Reader{
r: r, r: r,
d: d, d: d,
} }
@@ -23,14 +23,15 @@ func NewReader(r io.Reader, d decompress.Decompressor) *Reader {
func (r *Reader) advance() error { func (r *Reader) advance() error {
r.curOffset = 0 r.curOffset = 0
var size uint16 dat := make([]byte, 2)
err := binary.Read(r.r, binary.LittleEndian, &size) _, err := r.r.Read(dat)
if err != nil { if err != nil {
return err return err
} }
size := binary.LittleEndian.Uint16(dat)
realSize := size &^ 0x8000 realSize := size &^ 0x8000
r.dat = make([]byte, realSize) r.dat = make([]byte, realSize)
err = binary.Read(r.r, binary.LittleEndian, &r.dat) _, err = r.r.Read(r.dat)
if err != nil { if err != nil {
return err return err
} }
+17 -5
View File
@@ -1,15 +1,14 @@
package data package data
import ( import (
"encoding/binary"
"errors" "errors"
"io" "io"
"io/fs"
"math" "math"
"runtime" "runtime"
"sync" "sync"
"github.com/CalebQ42/squashfs/internal/decompress" "github.com/CalebQ42/squashfs/internal/decompress"
"github.com/CalebQ42/squashfs/internal/toreader"
) )
type FragReaderConstructor func() (io.Reader, error) type FragReaderConstructor func() (io.Reader, error)
@@ -23,10 +22,11 @@ type FullReader struct {
finalBlockSize uint64 finalBlockSize uint64
blockSize uint32 blockSize uint32
goroutineLimit uint16 goroutineLimit uint16
closed bool
} }
func NewFullReader(r io.ReaderAt, initialOffset int64, d decompress.Decompressor, sizes []uint32, finalBlockSize uint64, blockSize uint32) *FullReader { func NewFullReader(r io.ReaderAt, initialOffset int64, d decompress.Decompressor, sizes []uint32, finalBlockSize uint64, blockSize uint32) FullReader {
return &FullReader{ return FullReader{
r: r, r: r,
d: d, d: d,
sizes: sizes, sizes: sizes,
@@ -37,6 +37,15 @@ func NewFullReader(r io.ReaderAt, initialOffset int64, d decompress.Decompressor
} }
} }
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) { func (r *FullReader) AddFrag(frag FragReaderConstructor) {
r.frag = frag r.frag = frag
} }
@@ -69,7 +78,7 @@ func (r FullReader) process(index uint64, fileOffset uint64, pool *sync.Pool, re
return return
} }
ret.data = make([]byte, realSize) 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 { if r.sizes[index] == realSize {
ret.data, ret.err = r.d.Decompress(ret.data) ret.data, ret.err = r.d.Decompress(ret.data)
} }
@@ -77,6 +86,9 @@ func (r FullReader) process(index uint64, fileOffset uint64, pool *sync.Pool, re
} }
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 { // if wa, is := w.(io.WriterAt); is {
// return r.writeToWriteAt(wa) // return r.writeToWriteAt(wa)
// } // }
+13 -4
View File
@@ -1,8 +1,8 @@
package data package data
import ( import (
"encoding/binary"
"io" "io"
"io/fs"
"github.com/CalebQ42/squashfs/internal/decompress" "github.com/CalebQ42/squashfs/internal/decompress"
) )
@@ -17,10 +17,11 @@ type Reader struct {
curIndex uint64 curIndex uint64
finalBlockSize uint64 finalBlockSize uint64
blockSize uint32 blockSize uint32
closed bool
} }
func NewReader(r io.Reader, d decompress.Decompressor, sizes []uint32, finalBlockSize uint64, blockSize uint32) *Reader { func NewReader(r io.Reader, d decompress.Decompressor, sizes []uint32, finalBlockSize uint64, blockSize uint32) Reader {
return &Reader{ return Reader{
r: r, r: r,
d: d, d: d,
sizes: sizes, sizes: sizes,
@@ -54,7 +55,7 @@ func (r *Reader) advance() error {
return nil return nil
} }
r.dat = make([]byte, realSize) r.dat = make([]byte, realSize)
err = binary.Read(r.r, binary.LittleEndian, &r.dat) _, err = r.r.Read(r.dat)
if err != nil { if err != nil {
return err return err
} }
@@ -66,6 +67,9 @@ func (r *Reader) advance() error {
} }
func (r *Reader) Read(b []byte) (int, error) { func (r *Reader) Read(b []byte) (int, error) {
if r.closed {
return 0, fs.ErrClosed
}
curRead := 0 curRead := 0
var toRead int var toRead int
for curRead < len(b) { for curRead < len(b) {
@@ -83,6 +87,9 @@ func (r *Reader) Read(b []byte) (int, error) {
} }
func (r *Reader) Close() error { func (r *Reader) Close() error {
r.closed = true
r.r = nil
r.d = nil
if r.frag != nil { if r.frag != nil {
if l, ok := r.frag.(*io.LimitedReader); ok { if l, ok := r.frag.(*io.LimitedReader); ok {
if cl, ok := l.R.(io.Closer); ok { if cl, ok := l.R.(io.Closer); ok {
@@ -90,6 +97,8 @@ func (r *Reader) Close() error {
} }
} }
} }
r.frag = nil
r.sizes = nil
r.dat = nil r.dat = nil
return nil return nil
} }
+3 -3
View File
@@ -18,7 +18,7 @@ type Directory struct {
Entries []directory.Entry 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) i, err := r.InodeFromRef(ref)
if err != nil { if err != nil {
return Directory{}, err return Directory{}, err
@@ -44,7 +44,7 @@ func (r *Reader) directoryFromRef(ref uint64, name string) (Directory, error) {
if err != nil { if err != nil {
return Directory{}, err return Directory{}, err
} }
entries, err := directory.ReadDirectory(dirRdr, size) entries, err := directory.ReadDirectory(&dirRdr, size)
if err != nil { if err != nil {
return Directory{}, err return Directory{}, err
} }
@@ -54,7 +54,7 @@ func (r *Reader) directoryFromRef(ref uint64, name string) (Directory, error) {
}, nil }, 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) path = filepath.Clean(path)
if path == "." || path == "" { if path == "." || path == "" {
return d.FileBase, nil return d.FileBase, nil
+21 -21
View File
@@ -16,11 +16,11 @@ type FileBase struct {
Name string 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} 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) in, err := r.InodeFromEntry(e)
if err != nil { if err != nil {
return FileBase{}, err return FileBase{}, err
@@ -28,7 +28,7 @@ func (r *Reader) BaseFromEntry(e directory.Entry) (FileBase, error) {
return FileBase{Inode: in, Name: e.Name}, nil 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) in, err := r.InodeFromRef(ref)
if err != nil { if err != nil {
return FileBase{}, err return FileBase{}, err
@@ -36,19 +36,19 @@ func (r *Reader) BaseFromRef(ref uint64, name string) (FileBase, error) {
return FileBase{Inode: in, Name: name}, nil 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) 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) 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 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 blockStart uint32
var size uint32 var size uint32
var offset uint16 var offset uint16
@@ -70,23 +70,23 @@ func (b *FileBase) ToDir(r *Reader) (Directory, error) {
if err != nil { if err != nil {
return Directory{}, err return Directory{}, err
} }
entries, err := directory.ReadDirectory(dirRdr, size) entries, err := directory.ReadDirectory(&dirRdr, size)
if err != nil { if err != nil {
return Directory{}, err return Directory{}, err
} }
return Directory{ return Directory{
FileBase: *b, FileBase: b,
Entries: entries, Entries: entries,
}, nil }, nil
} }
func (b *FileBase) IsRegular() bool { func (b FileBase) IsRegular() bool {
return b.Inode.Type == inode.Fil || b.Inode.Type == inode.EFil 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() { 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 blockStart uint64
var fragIndex uint32 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 := 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)) 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) outRdr := data.NewReader(toreader.NewReader(r.r, int64(blockStart)), r.d, sizes, fragSize, r.Superblock.BlockSize)
if fragIndex != 0xffffffff { if fragIndex != 0xffffffff {
f, err := frag() f, err := frag()
if err != nil { if err != nil {
return nil, nil, err return data.Reader{}, data.FullReader{}, err
} }
outRdr.AddFrag(f) outRdr.AddFrag(f)
} }
@@ -130,9 +130,9 @@ func (b *FileBase) GetRegFileReaders(r *Reader) (*data.Reader, *data.FullReader,
return outRdr, outFull, nil 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() { if !b.IsRegular() {
return nil, errors.New("not a regular file") return data.FullReader{}, errors.New("not a regular file")
} }
var blockStart uint64 var blockStart uint64
var fragIndex uint32 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 := 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)) frag.Read(make([]byte, fragOffset))
return io.LimitReader(frag, int64(fragSize)), nil return io.LimitReader(&frag, int64(fragSize)), nil
}) })
} }
return outFull, 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() { if !b.IsRegular() {
return nil, errors.New("not a regular file") return data.Reader{}, errors.New("not a regular file")
} }
var blockStart uint64 var blockStart uint64
var fragIndex uint32 var fragIndex uint32
@@ -193,11 +193,11 @@ func (b *FileBase) GetReader(r *Reader) (*data.Reader, error) {
if fragIndex != 0xffffffff { if fragIndex != 0xffffffff {
ent, err := r.fragEntry(fragIndex) ent, err := r.fragEntry(fragIndex)
if err != nil { 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 := 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)) frag.Read(make([]byte, fragOffset))
outRdr.AddFrag(io.LimitReader(frag, int64(fragSize))) outRdr.AddFrag(io.LimitReader(&frag, int64(fragSize)))
} }
return outRdr, nil return outRdr, nil
} }
+4 -4
View File
@@ -7,7 +7,7 @@ import (
"github.com/CalebQ42/squashfs/low/inode" "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 offset, meta := (ref>>16)+r.Superblock.InodeTableStart, ref&0xFFFF
rdr := metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d) rdr := metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
defer rdr.Close() defer rdr.Close()
@@ -15,12 +15,12 @@ func (r *Reader) InodeFromRef(ref uint64) (inode.Inode, error) {
if err != nil { if err != nil {
return inode.Inode{}, err 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) rdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.Superblock.InodeTableStart)+int64(e.BlockStart)), r.d)
defer rdr.Close() defer rdr.Close()
rdr.Read(make([]byte, e.Offset)) 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 ParentNum uint32
} }
type eDirectoryInit struct { type EDirectory struct {
LinkCount uint32 LinkCount uint32
Size uint32 Size uint32
BlockStart uint32 BlockStart uint32
@@ -21,42 +21,55 @@ type eDirectoryInit struct {
IndCount uint16 IndCount uint16
Offset uint16 Offset uint16
XattrInd uint32 XattrInd uint32
} Indexes []DirectoryIndex
type EDirectory struct {
eDirectoryInit
Indexes []DirectoryIndex
}
type directoryIndexInit struct {
Ind uint32
Start uint32
NameSize uint32
} }
type DirectoryIndex struct { type DirectoryIndex struct {
directoryIndexInit Ind uint32
Name []byte Start uint32
NameSize uint32
Name []byte
} }
func ReadDir(r io.Reader) (d Directory, err error) { 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 return
} }
func ReadEDir(r io.Reader) (d EDirectory, err error) { 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 { if err != nil {
return 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) d.Indexes = make([]DirectoryIndex, d.IndCount)
for i := range d.Indexes { for i := range d.IndCount {
err = binary.Read(r, binary.LittleEndian, &d.Indexes[i].directoryIndexInit) dat = make([]byte, 12)
_, err = r.Read(dat)
if err != nil { if err != nil {
return 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) 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 { if err != nil {
return return
} }
+15 -7
View File
@@ -6,15 +6,11 @@ import (
"math" "math"
) )
type fileInit struct { type File struct {
BlockStart uint32 BlockStart uint32
FragInd uint32 FragInd uint32
FragOffset uint32 FragOffset uint32
Size uint32 Size uint32
}
type File struct {
fileInit
BlockSizes []uint32 BlockSizes []uint32
} }
@@ -34,16 +30,28 @@ type EFile struct {
} }
func ReadFile(r io.Reader, blockSize uint32) (f File, err error) { 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 { if err != nil {
return 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))) toRead := int(math.Floor(float64(f.Size) / float64(blockSize)))
if f.FragInd == 0xFFFFFFFF && f.Size%blockSize > 0 { if f.FragInd == 0xFFFFFFFF && f.Size%blockSize > 0 {
toRead++ toRead++
} }
dat = make([]byte, toRead*4)
_, err = r.Read(dat)
if err != nil {
return
}
f.BlockSizes = make([]uint32, toRead) 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 return
} }
+21 -19
View File
@@ -39,21 +39,20 @@ type Reader struct {
Superblock superblock Superblock superblock
} }
func NewReader(r io.ReaderAt) (rdr *Reader, err error) { func NewReader(r io.ReaderAt) (rdr Reader, err error) {
rdr = new(Reader)
rdr.r = r rdr.r = r
err = binary.Read(toreader.NewReader(r, 0), binary.LittleEndian, &rdr.Superblock) err = binary.Read(toreader.NewReader(r, 0), binary.LittleEndian, &rdr.Superblock)
if err != nil { 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() { if !rdr.Superblock.ValidMagic() {
return nil, ErrorMagic return rdr, ErrorMagic
} }
if !rdr.Superblock.ValidBlockLog() { if !rdr.Superblock.ValidBlockLog() {
return nil, ErrorLog return rdr, ErrorLog
} }
if !rdr.Superblock.ValidVersion() { if !rdr.Superblock.ValidVersion() {
return nil, ErrorVersion return rdr, ErrorVersion
} }
switch rdr.Superblock.CompType { switch rdr.Superblock.CompType {
case ZlibCompression: case ZlibCompression:
@@ -61,25 +60,25 @@ func NewReader(r io.ReaderAt) (rdr *Reader, err error) {
case LZMACompression: case LZMACompression:
rdr.d, err = decompress.NewLzma() rdr.d, err = decompress.NewLzma()
if err != nil { if err != nil {
return nil, err return rdr, err
} }
case LZOCompression: case LZOCompression:
rdr.d, err = decompress.NewLzo() rdr.d, err = decompress.NewLzo()
if err != nil { if err != nil {
return nil, err return rdr, err
} }
case XZCompression: case XZCompression:
rdr.d = decompress.Xz{} rdr.d = decompress.NewXz()
case LZ4Compression: case LZ4Compression:
rdr.d = decompress.Lz4{} rdr.d = decompress.NewLz4()
case ZSTDCompression: case ZSTDCompression:
rdr.d = decompress.Zstd{} rdr.d = decompress.Zstd{}
default: 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, "") rdr.Root, err = rdr.directoryFromRef(rdr.Superblock.RootInodeRef, "")
if err != nil { 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 return
} }
@@ -105,7 +104,8 @@ func (r *Reader) Id(i uint16) (uint32, error) {
var idsToRead uint16 var idsToRead uint16
var idsTmp []uint32 var idsTmp []uint32
var err error 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++ { 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) err = binary.Read(toreader.NewReader(r.r, int64(r.Superblock.IdTableStart)+int64(8*i)), binary.LittleEndian, &offset)
if err != nil { if err != nil {
@@ -114,7 +114,7 @@ func (r *Reader) Id(i uint16) (uint32, error) {
idsToRead = min(r.Superblock.IdCount-uint16(len(r.idTable)), 2048) idsToRead = min(r.Superblock.IdCount-uint16(len(r.idTable)), 2048)
idsTmp = make([]uint32, idsToRead) idsTmp = make([]uint32, idsToRead)
rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d) 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() rdr.Close()
if err != nil { if err != nil {
return 0, err return 0, err
@@ -145,7 +145,8 @@ func (r *Reader) fragEntry(i uint32) (fragEntry, error) {
var fragsToRead uint32 var fragsToRead uint32
var fragsTmp []fragEntry var fragsTmp []fragEntry
var err error 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++ { 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) err = binary.Read(toreader.NewReader(r.r, int64(r.Superblock.FragTableStart)+int64(8*i)), binary.LittleEndian, &offset)
if err != nil { if err != nil {
@@ -154,7 +155,7 @@ func (r *Reader) fragEntry(i uint32) (fragEntry, error) {
fragsToRead = min(r.Superblock.FragCount-uint32(len(r.fragTable)), 512) fragsToRead = min(r.Superblock.FragCount-uint32(len(r.fragTable)), 512)
fragsTmp = make([]fragEntry, fragsToRead) fragsTmp = make([]fragEntry, fragsToRead)
rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d) rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
err = binary.Read(rdr, binary.LittleEndian, &fragsTmp) err = binary.Read(&rdr, binary.LittleEndian, &fragsTmp)
rdr.Close() rdr.Close()
if err != nil { if err != nil {
return fragEntry{}, err return fragEntry{}, err
@@ -188,7 +189,8 @@ func (r *Reader) inodeRef(i uint32) (uint64, error) {
var refsToRead uint32 var refsToRead uint32
var refsTmp []uint64 var refsTmp []uint64
var err error 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++ { 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) err = binary.Read(toreader.NewReader(r.r, int64(r.Superblock.ExportTableStart)+int64(8*i)), binary.LittleEndian, &offset)
if err != nil { if err != nil {
@@ -197,7 +199,7 @@ func (r *Reader) inodeRef(i uint32) (uint64, error) {
refsToRead = min(r.Superblock.InodeCount-uint32(len(r.exportTable)), 1024) refsToRead = min(r.Superblock.InodeCount-uint32(len(r.exportTable)), 1024)
refsTmp = make([]uint64, refsToRead) refsTmp = make([]uint64, refsToRead)
rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d) 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() rdr.Close()
if err != nil { if err != nil {
return 0, err return 0, err
@@ -207,7 +209,7 @@ func (r *Reader) inodeRef(i uint32) (uint64, error) {
return r.exportTable[i], nil 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) ref, err := r.inodeRef(i)
if err != nil { if err != nil {
return inode.Inode{}, err return inode.Inode{}, err
+4 -4
View File
@@ -77,7 +77,7 @@ func TestReader(t *testing.T) {
path := filepath.Join(tmpDir, "extractTest") path := filepath.Join(tmpDir, "extractTest")
os.RemoveAll(path) os.RemoveAll(path)
os.MkdirAll(path, 0777) os.MkdirAll(path, 0777)
err = extractToDir(rdr, &rdr.Root.FileBase, path) err = extractToDir(rdr, rdr.Root.FileBase, path)
t.Fatal(err) t.Fatal(err)
} }
@@ -101,11 +101,11 @@ func TestSingleFile(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = extractToDir(rdr, &b, path) err = extractToDir(rdr, b, path)
t.Fatal(err) t.Fatal(err)
} }
func extractToDir(rdr *Reader, b *FileBase, folder string) error { func extractToDir(rdr Reader, b FileBase, folder string) error {
path := filepath.Join(folder, b.Name) path := filepath.Join(folder, b.Name)
if b.IsDir() { if b.IsDir() {
d, err := b.ToDir(rdr) d, err := b.ToDir(rdr)
@@ -122,7 +122,7 @@ func extractToDir(rdr *Reader, b *FileBase, folder string) error {
if err != nil { if err != nil {
return err return err
} }
err = extractToDir(rdr, &nestBast, path) err = extractToDir(rdr, nestBast, path)
if err != nil { if err != nil {
return err return err
} }
+8 -8
View File
@@ -9,26 +9,26 @@ import (
) )
type Reader struct { type Reader struct {
*FS FS
Low squashfslow.Reader Low squashfslow.Reader
} }
func NewReader(r io.ReaderAt) (*Reader, error) { func NewReader(r io.ReaderAt) (Reader, error) {
rdr, err := squashfslow.NewReader(r) rdr, err := squashfslow.NewReader(r)
if err != nil { if err != nil {
return nil, err return Reader{}, err
} }
out := &Reader{ out := Reader{
Low: *rdr, Low: rdr,
} }
out.FS = &FS{ out.FS = FS{
d: rdr.Root, d: rdr.Root,
r: out, r: &out,
} }
return out, nil return out, nil
} }
func NewReaderAtOffset(r io.ReaderAt, offset int64) (*Reader, error) { func NewReaderAtOffset(r io.ReaderAt, offset int64) (Reader, error) {
return NewReader(toreader.NewOffsetReader(r, offset)) return NewReader(toreader.NewOffsetReader(r, offset))
} }
Executable
BIN
View File
Binary file not shown.
+25 -5
View File
@@ -17,7 +17,7 @@ import (
const ( const (
squashfsURL = "https://darkstorm.tech/files/LinuxPATest.sfs" squashfsURL = "https://darkstorm.tech/files/LinuxPATest.sfs"
squashfsName = "LinuxPATest.sfs" squashfsName = "tensorflow.sqfs"
) )
func preTest(dir string) (fil *os.File, err error) { func preTest(dir string) (fil *os.File, err error) {
@@ -68,6 +68,26 @@ func TestMisc(t *testing.T) {
// t.Fatal("UM") // t.Fatal("UM")
} }
func BenchmarkExtract(b *testing.B) {
tmpDir := "testing"
fil, err := preTest(tmpDir)
if err != nil {
b.Fatal(err)
}
libPath := filepath.Join(tmpDir, "ExtractLib")
os.RemoveAll(libPath)
op := FastOptions()
op.IgnorePerm = true
rdr, err := NewReader(fil)
if err != nil {
b.Fatal(err)
}
err = rdr.ExtractWithOptions(libPath, op)
if err != nil {
b.Fatal(err)
}
}
func BenchmarkRace(b *testing.B) { func BenchmarkRace(b *testing.B) {
tmpDir := "testing" tmpDir := "testing"
fil, err := preTest(tmpDir) fil, err := preTest(tmpDir)
@@ -91,7 +111,7 @@ func BenchmarkRace(b *testing.B) {
b.Fatal(err) b.Fatal(err)
} }
libTime = time.Since(start) libTime = time.Since(start)
cmd := exec.Command("unsquashfs", "-d", unsquashPath, fil.Name()) cmd := exec.Command("unsquashfs", "-q", "-d", unsquashPath, fil.Name())
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
start = time.Now() start = time.Now()
@@ -167,7 +187,7 @@ func TestExtractQuick(t *testing.T) {
} }
} }
var filePath = "Start.exe" var filePath = "usr/sbin/add-shell"
func TestSingleFile(t *testing.T) { func TestSingleFile(t *testing.T) {
tmpDir := "testing" tmpDir := "testing"
@@ -175,7 +195,7 @@ func TestSingleFile(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
os.Remove(filepath.Join(tmpDir, filePath)) os.RemoveAll("testing/stuff")
rdr, err := NewReader(fil) rdr, err := NewReader(fil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -186,7 +206,7 @@ func TestSingleFile(t *testing.T) {
} }
op := DefaultOptions() op := DefaultOptions()
op.Verbose = true op.Verbose = true
err = f.(*File).ExtractWithOptions("testing", op) err = f.(*File).ExtractWithOptions("testing/stuff", op)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }