Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ebbbc9e87e | |||
| 155999a8e3 | |||
| 7930f4402b | |||
| ada61a391c | |||
| f32cb520dc | |||
| f991ddb1d4 | |||
| 4c8c9f0b47 | |||
| 8a2556ea0d | |||
| 33156751ca | |||
| 6224c4be41 | |||
| 6b0e9ef2c6 | |||
| 4490fc3873 |
@@ -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
|
||||||
|
|
||||||
|
|||||||
+73
-25
@@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -12,6 +11,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/CalebQ42/squashfs"
|
"github.com/CalebQ42/squashfs"
|
||||||
|
squashfslow "github.com/CalebQ42/squashfs/low"
|
||||||
)
|
)
|
||||||
|
|
||||||
func userName(uid int, numeric bool) string {
|
func userName(uid int, numeric bool) string {
|
||||||
@@ -36,32 +36,84 @@ func groupName(gid int, numeric bool) string {
|
|||||||
return gs
|
return gs
|
||||||
}
|
}
|
||||||
|
|
||||||
func printEntry(root, path string, d fs.DirEntry, numeric bool) {
|
var hardLinks = make(map[uint32]string)
|
||||||
fi, _ := d.Info()
|
|
||||||
|
func printFile(rdr *squashfs.Reader, path string, f *squashfs.File) {
|
||||||
|
path = filepath.Join(path, f.Low.Name)
|
||||||
|
fi, _ := f.Stat()
|
||||||
sfi := fi.(squashfs.FileInfo)
|
sfi := fi.(squashfs.FileInfo)
|
||||||
owner := fmt.Sprintf("%s/%s",
|
owner := fmt.Sprintf("%s/%s",
|
||||||
userName(sfi.Uid(), numeric),
|
userName(sfi.Uid(), *numeric),
|
||||||
groupName(sfi.Gid(), numeric))
|
groupName(sfi.Gid(), *numeric))
|
||||||
link := ""
|
var link string
|
||||||
|
var isHardLink bool
|
||||||
|
if *showHardLinks {
|
||||||
|
link, isHardLink = hardLinks[f.Low.Inode.Num]
|
||||||
|
if !isHardLink {
|
||||||
|
hardLinks[f.Low.Inode.Num] = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var size int64
|
||||||
|
if isHardLink {
|
||||||
|
size = 0
|
||||||
|
} else {
|
||||||
|
size = fi.Size()
|
||||||
|
}
|
||||||
if sfi.IsSymlink() {
|
if sfi.IsSymlink() {
|
||||||
link = " -> " + sfi.SymlinkPath()
|
link = " -> " + sfi.SymlinkPath()
|
||||||
|
} else if isHardLink {
|
||||||
|
link = " link to " + link
|
||||||
}
|
}
|
||||||
fmt.Printf("%s %s %*d %s %s%s\n",
|
fmt.Printf("%s %s %*d %s %s%s\n",
|
||||||
strings.ToLower(fi.Mode().String()),
|
strings.ToLower(fi.Mode().String()),
|
||||||
owner, 26-len(owner), fi.Size(),
|
owner, 26-len(owner), size,
|
||||||
fi.ModTime().Format("2006-01-02 15:04"),
|
fi.ModTime().Format("2006-01-02 15:04"),
|
||||||
filepath.Join(root, path), link)
|
path, link)
|
||||||
|
if f.IsDir() {
|
||||||
|
fs, _ := f.FS()
|
||||||
|
printDir(rdr, path, fs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printDir(rdr *squashfs.Reader, path string, f squashfs.FS) {
|
||||||
|
var base squashfslow.FileBase
|
||||||
|
var fil squashfs.File
|
||||||
|
var err error
|
||||||
|
for _, e := range f.LowDir.Entries {
|
||||||
|
base, err = rdr.Low.BaseFromEntry(e)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fil = rdr.FileFromBase(base, f)
|
||||||
|
printFile(rdr, path, &fil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
verbose *bool
|
||||||
|
list *bool
|
||||||
|
long *bool
|
||||||
|
numeric *bool
|
||||||
|
offset *int64
|
||||||
|
ignore *bool
|
||||||
|
file *string
|
||||||
|
showHardLinks *bool
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
verbose := flag.Bool("v", false, "Verbose")
|
verbose = flag.Bool("v", false, "Verbose")
|
||||||
list := flag.Bool("l", false, "List")
|
list = flag.Bool("l", false, "List")
|
||||||
long := flag.Bool("ll", false, "List with attributes")
|
long = flag.Bool("ll", false, "List with attributes")
|
||||||
numeric := flag.Bool("lln", false, "List with attributes and numeric ids")
|
numeric = flag.Bool("lln", false, "List with attributes and numeric ids")
|
||||||
offset := flag.Int64("o", 0, "Offset")
|
showHardLinks = flag.Bool("show-hard-links", false, "When used with ll or lln, shows hard links")
|
||||||
ignore := flag.Bool("ip", false, "Ignore Permissions and extract all files/folders with 0755")
|
offset = flag.Int64("o", 0, "Offset")
|
||||||
|
ignore = flag.Bool("ip", false, "Ignore Permissions and extract all files/folders with 0755")
|
||||||
|
file = flag.String("e", "", "File or folder to extract")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if len(flag.Args()) < 2 {
|
if (*list || *long || *numeric) && flag.NArg() < 1 {
|
||||||
|
fmt.Println("Please provide a file name")
|
||||||
|
os.Exit(0)
|
||||||
|
} else if (!*list && !*long && !*numeric) && flag.NArg() < 2 {
|
||||||
fmt.Println("Please provide a file name and extraction path")
|
fmt.Println("Please provide a file name and extraction path")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
@@ -73,26 +125,22 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if *list || *long || *numeric {
|
extractFil := r.File()
|
||||||
root := flag.Arg(1)
|
if *file != "" {
|
||||||
fs.WalkDir(r, ".", func(path string, d fs.DirEntry, err error) error {
|
extractFil, err = r.OpenFile(*file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if *long || *numeric {
|
|
||||||
printEntry(root, path, d, *numeric)
|
|
||||||
} else {
|
|
||||||
fmt.Println(filepath.Join(root, path))
|
|
||||||
}
|
}
|
||||||
return nil
|
if *list || *long || *numeric {
|
||||||
})
|
printFile(&r, "", extractFil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
op := squashfs.DefaultOptions()
|
op := squashfs.DefaultOptions()
|
||||||
op.Verbose = *verbose
|
op.Verbose = *verbose
|
||||||
op.IgnorePerm = *ignore
|
op.IgnorePerm = *ignore
|
||||||
n := time.Now()
|
n := time.Now()
|
||||||
err = r.ExtractWithOptions(flag.Arg(1), op)
|
err = extractFil.ExtractWithOptions(flag.Arg(1), op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
Low 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,
|
Low: 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.Low.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{LowDir: 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,22 +74,22 @@ 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.Low.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.Low.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.Low.Inode.Type == inode.Sym || f.Low.Inode.Type == inode.ESym
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) Mode() fs.FileMode {
|
func (f File) Mode() fs.FileMode {
|
||||||
return f.b.Inode.Mode()
|
return f.Low.Inode.Mode()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read reads the data from the file. Only works if file is a normal file.
|
// Read reads the data from the file. Only works if file is a normal file.
|
||||||
@@ -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.Low.ToDir(f.r.Low)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -141,25 +139,25 @@ 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.Low.Uid(&f.r.Low)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
gid, err := f.b.Gid(&f.r.Low)
|
gid, err := f.Low.Gid(&f.r.Low)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newFileInfo(f.b.Name, uid, gid, &f.b.Inode), nil
|
return newFileInfo(f.Low.Name, uid, gid, &f.Low.Inode), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.Low.Inode.Type {
|
||||||
case inode.Sym:
|
case inode.Sym:
|
||||||
return string(f.b.Inode.Data.(inode.Symlink).Target)
|
return string(f.Low.Inode.Data.(inode.Symlink).Target)
|
||||||
case inode.ESym:
|
case inode.ESym:
|
||||||
return string(f.b.Inode.Data.(inode.ESymlink).Target)
|
return string(f.Low.Inode.Data.(inode.ESymlink).Target)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -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,37 +179,43 @@ 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.Low.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.Low.Inode.Type {
|
||||||
case inode.Char, inode.Block:
|
case inode.Char, inode.Block:
|
||||||
dev = f.b.Inode.Data.(inode.Device).Dev
|
dev = f.Low.Inode.Data.(inode.Device).Dev
|
||||||
case inode.EChar, inode.EBlock:
|
case inode.EChar, inode.EBlock:
|
||||||
dev = f.b.Inode.Data.(inode.EDevice).Dev
|
dev = f.Low.Inode.Data.(inode.EDevice).Dev
|
||||||
}
|
}
|
||||||
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.LowDir.Name == "" {
|
||||||
return f.b.Name
|
return f.Low.Name
|
||||||
}
|
}
|
||||||
return filepath.Join(f.parent.path(), f.b.Name)
|
return filepath.Join(f.parent.path(), f.Low.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
@@ -225,9 +229,9 @@ func (f *File) ExtractWithOptions(path string, op *ExtractionOptions) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch f.b.Inode.Type {
|
switch f.Low.Inode.Type {
|
||||||
case inode.Dir, inode.EDir:
|
case inode.Dir, inode.EDir:
|
||||||
d, err := f.b.ToDir(&f.r.Low)
|
d, err := f.Low.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)
|
||||||
@@ -285,7 +289,7 @@ func (f *File) ExtractWithOptions(path string, op *ExtractionOptions) error {
|
|||||||
return errors.Join(errors.New("failed to extract folder: "+path), errors.Join(errCache...))
|
return errors.Join(errors.New("failed to extract folder: "+path), errors.Join(errCache...))
|
||||||
}
|
}
|
||||||
case inode.Fil, inode.EFil:
|
case inode.Fil, inode.EFil:
|
||||||
path = filepath.Join(path, f.b.Name)
|
path = filepath.Join(path, f.Low.Name)
|
||||||
outFil, err := os.Create(path)
|
outFil, err := os.Create(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if op.Verbose {
|
if op.Verbose {
|
||||||
@@ -294,7 +298,7 @@ func (f *File) ExtractWithOptions(path string, op *ExtractionOptions) error {
|
|||||||
return errors.Join(errors.New("failed to create file: "+path), err)
|
return errors.Join(errors.New("failed to create file: "+path), err)
|
||||||
}
|
}
|
||||||
defer outFil.Close()
|
defer outFil.Close()
|
||||||
full, err := f.b.GetFullReader(&f.r.Low)
|
full, err := f.Low.GetFullReader(&f.r.Low)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if op.Verbose {
|
if op.Verbose {
|
||||||
log.Println("Failed to create full reader for", path)
|
log.Println("Failed to create full reader for", path)
|
||||||
@@ -320,11 +324,11 @@ func (f *File) ExtractWithOptions(path string, op *ExtractionOptions) error {
|
|||||||
return errors.New("failed to get symlink's file")
|
return errors.New("failed to get symlink's file")
|
||||||
}
|
}
|
||||||
fil := filTmp.(*File)
|
fil := filTmp.(*File)
|
||||||
fil.b.Name = f.b.Name
|
fil.Low.Name = f.Low.Name
|
||||||
err := fil.ExtractWithOptions(path, op)
|
err := fil.ExtractWithOptions(path, op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if op.Verbose {
|
if op.Verbose {
|
||||||
log.Println("Failed to extract symlink's file:", filepath.Join(path, f.b.Name))
|
log.Println("Failed to extract symlink's file:", filepath.Join(path, f.Low.Name))
|
||||||
}
|
}
|
||||||
return errors.Join(errors.New("failed to extract symlink's file: "+path), err)
|
return errors.Join(errors.New("failed to extract symlink's file: "+path), err)
|
||||||
}
|
}
|
||||||
@@ -347,7 +351,7 @@ func (f *File) ExtractWithOptions(path string, op *ExtractionOptions) error {
|
|||||||
return errors.Join(errors.New("failed to extract symlink's file: "+extractLoc), err)
|
return errors.Join(errors.New("failed to extract symlink's file: "+extractLoc), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
path = filepath.Join(path, f.b.Name)
|
path = filepath.Join(path, f.Low.Name)
|
||||||
err := os.Symlink(f.SymlinkPath(), path)
|
err := os.Symlink(f.SymlinkPath(), path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if op.Verbose {
|
if op.Verbose {
|
||||||
@@ -370,9 +374,9 @@ func (f *File) ExtractWithOptions(path string, op *ExtractionOptions) error {
|
|||||||
}
|
}
|
||||||
return errors.Join(errors.New("mknot command not found"), err)
|
return errors.Join(errors.New("mknot command not found"), err)
|
||||||
}
|
}
|
||||||
path = filepath.Join(path, f.b.Name)
|
path = filepath.Join(path, f.Low.Name)
|
||||||
var typ string
|
var typ string
|
||||||
switch f.b.Inode.Type {
|
switch f.Low.Inode.Type {
|
||||||
case inode.Char, inode.EChar:
|
case inode.Char, inode.EChar:
|
||||||
typ = "c"
|
typ = "c"
|
||||||
case inode.Block, inode.EBlock:
|
case inode.Block, inode.EBlock:
|
||||||
@@ -408,7 +412,7 @@ func (f *File) ExtractWithOptions(path string, op *ExtractionOptions) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return errors.New("Unsupported file type. Inode type: " + strconv.Itoa(int(f.b.Inode.Type)))
|
return errors.New("Unsupported file type. Inode type: " + strconv.Itoa(int(f.Low.Inode.Type)))
|
||||||
}
|
}
|
||||||
if op.Verbose {
|
if op.Verbose {
|
||||||
log.Println(f.path(), "extracted to", path)
|
log.Println(f.path(), "extracted to", path)
|
||||||
@@ -416,7 +420,7 @@ func (f *File) ExtractWithOptions(path string, op *ExtractionOptions) error {
|
|||||||
if op.IgnorePerm {
|
if op.IgnorePerm {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
uid, err := f.b.Uid(&f.r.Low)
|
uid, err := f.Low.Uid(&f.r.Low)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if op.Verbose {
|
if op.Verbose {
|
||||||
log.Println("Failed to get uid for", path)
|
log.Println("Failed to get uid for", path)
|
||||||
@@ -424,7 +428,7 @@ func (f *File) ExtractWithOptions(path string, op *ExtractionOptions) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
gid, err := f.b.Gid(&f.r.Low)
|
gid, err := f.Low.Gid(&f.r.Low)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if op.Verbose {
|
if op.Verbose {
|
||||||
log.Println("Failed to get gid for", path)
|
log.Println("Failed to get gid for", path)
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ import (
|
|||||||
type FS struct {
|
type FS struct {
|
||||||
r *Reader
|
r *Reader
|
||||||
parent *FS
|
parent *FS
|
||||||
d squashfslow.Directory
|
LowDir squashfslow.Directory
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
LowDir: d,
|
||||||
r: r,
|
r: r,
|
||||||
parent: parent,
|
parent: &parent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,10 +42,10 @@ func (f *FS) Glob(pattern string) (out []string, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
split := strings.Split(pattern, "/")
|
split := strings.Split(pattern, "/")
|
||||||
for i := range f.d.Entries {
|
for i := range f.LowDir.Entries {
|
||||||
if match, _ := path.Match(split[0], f.d.Entries[i].Name); match {
|
if match, _ := path.Match(split[0], f.LowDir.Entries[i].Name); match {
|
||||||
if len(split) == 1 {
|
if len(split) == 1 {
|
||||||
out = append(out, f.d.Entries[i].Name)
|
out = append(out, f.LowDir.Entries[i].Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sub, err := f.Sub(split[0])
|
sub, err := f.Sub(split[0])
|
||||||
@@ -81,7 +81,7 @@ func (f *FS) Glob(pattern string) (out []string, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := range subGlob {
|
for i := range subGlob {
|
||||||
subGlob[i] = f.d.Name + "/" + subGlob[i]
|
subGlob[i] = f.LowDir.Name + "/" + subGlob[i]
|
||||||
}
|
}
|
||||||
out = append(out, subGlob...)
|
out = append(out, subGlob...)
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,11 @@ 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) {
|
||||||
|
return f.OpenFile(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FS) OpenFile(name string) (*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{
|
||||||
@@ -111,10 +115,10 @@ func (f *FS) Open(name string) (fs.File, error) {
|
|||||||
Err: fs.ErrNotExist,
|
Err: fs.ErrNotExist,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return f.parent.Open(strings.Join(split[1:], "/"))
|
return f.parent.OpenFile(strings.Join(split[1:], "/"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i, found := slices.BinarySearchFunc(f.d.Entries, split[0], func(e directory.Entry, name string) int {
|
i, found := slices.BinarySearchFunc(f.LowDir.Entries, split[0], func(e directory.Entry, name string) int {
|
||||||
return strings.Compare(e.Name, name)
|
return strings.Compare(e.Name, name)
|
||||||
})
|
})
|
||||||
if !found {
|
if !found {
|
||||||
@@ -124,13 +128,13 @@ func (f *FS) Open(name string) (fs.File, error) {
|
|||||||
Err: fs.ErrNotExist,
|
Err: fs.ErrNotExist,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b, err := f.r.Low.BaseFromEntry(f.d.Entries[i])
|
b, err := f.r.Low.BaseFromEntry(f.LowDir.Entries[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(split) == 1 {
|
if len(split) == 1 {
|
||||||
return &File{
|
return &File{
|
||||||
b: b,
|
Low: b,
|
||||||
r: f.r,
|
r: f.r,
|
||||||
parent: f,
|
parent: f,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -142,16 +146,16 @@ 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
|
||||||
}
|
}
|
||||||
return f.r.FSFromDirectory(d, f).Open(strings.Join(split[1:], "/"))
|
return f.r.FSFromDirectory(d, f).OpenFile(strings.Join(split[1:], "/"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 +175,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 +198,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 +218,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,28 +246,34 @@ 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{
|
return &File{
|
||||||
b: f.d.FileBase,
|
Low: f.LowDir.FileBase,
|
||||||
parent: f.parent,
|
parent: *f.parent,
|
||||||
|
r: f.r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &File{
|
||||||
|
Low: f.LowDir.FileBase,
|
||||||
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.LowDir.Name
|
||||||
}
|
}
|
||||||
return filepath.Join(f.parent.path(), f.d.Name)
|
return filepath.Join(f.parent.path(), f.LowDir.Name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,17 +2,31 @@ package decompress
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/zlib"
|
|
||||||
"io"
|
"io"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/klauspost/compress/zlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Zlib struct{}
|
type Zlib struct {
|
||||||
|
pool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
func (z Zlib) Decompress(data []byte) ([]byte, error) {
|
func NewZlib() *Zlib {
|
||||||
rdr, err := zlib.NewReader(bytes.NewReader(data))
|
return &Zlib{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Zlib) Decompress(data []byte) ([]byte, error) {
|
||||||
|
rdr := z.pool.Get()
|
||||||
|
defer z.pool.Put(rdr)
|
||||||
|
var err error
|
||||||
|
if rdr == nil {
|
||||||
|
rdr, err = zlib.NewReader(bytes.NewReader(data))
|
||||||
|
} else {
|
||||||
|
err = rdr.(zlib.Resetter).Reset(bytes.NewReader(data), nil)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rdr.Close()
|
return io.ReadAll(rdr.(io.ReadCloser))
|
||||||
return io.ReadAll(rdr)
|
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-10
@@ -1,19 +1,20 @@
|
|||||||
package decompress
|
package decompress
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/klauspost/compress/zstd"
|
"github.com/klauspost/compress/zstd"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Zstd struct{}
|
type Zstd struct {
|
||||||
|
rdr *zstd.Decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewZstd() Zstd {
|
||||||
|
rdr, _ := zstd.NewReader(nil, zstd.WithDecoderLowmem(true))
|
||||||
|
return Zstd{
|
||||||
|
rdr: rdr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (z Zstd) Decompress(data []byte) ([]byte, error) {
|
func (z Zstd) Decompress(data []byte) ([]byte, error) {
|
||||||
rdr, err := zstd.NewReader(bytes.NewReader(data))
|
return z.rdr.DecodeAll(data, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rdr.Close()
|
|
||||||
return io.ReadAll(rdr)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-15
@@ -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
|
||||||
}
|
|
||||||
|
|
||||||
type EDirectory struct {
|
|
||||||
eDirectoryInit
|
|
||||||
Indexes []DirectoryIndex
|
Indexes []DirectoryIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
type directoryIndexInit struct {
|
type DirectoryIndex struct {
|
||||||
Ind uint32
|
Ind uint32
|
||||||
Start uint32
|
Start uint32
|
||||||
NameSize uint32
|
NameSize uint32
|
||||||
}
|
|
||||||
|
|
||||||
type DirectoryIndex struct {
|
|
||||||
directoryIndexInit
|
|
||||||
Name []byte
|
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
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-21
@@ -39,47 +39,46 @@ 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:
|
||||||
rdr.d = decompress.Zlib{}
|
rdr.d = decompress.NewZlib()
|
||||||
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.NewZstd()
|
||||||
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
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
LowDir: 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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-8
@@ -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,24 @@ 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)
|
||||||
|
rdr, err := NewReader(fil)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
err = rdr.ExtractWithOptions(libPath, FastOptions())
|
||||||
|
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)
|
||||||
@@ -79,19 +97,17 @@ func BenchmarkRace(b *testing.B) {
|
|||||||
os.RemoveAll(libPath)
|
os.RemoveAll(libPath)
|
||||||
os.RemoveAll(unsquashPath)
|
os.RemoveAll(unsquashPath)
|
||||||
var libTime, unsquashTime time.Duration
|
var libTime, unsquashTime time.Duration
|
||||||
op := FastOptions()
|
|
||||||
op.IgnorePerm = true
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
rdr, err := NewReader(fil)
|
rdr, err := NewReader(fil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
err = rdr.ExtractWithOptions(libPath, op)
|
err = rdr.ExtractWithOptions(libPath, FastOptions())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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 +183,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 +191,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 +202,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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user