Further work on extracting

Extracting files is threaded with goroutines.
Extracting also sets the proper UID and GUID
Made recursive children getting threaded with goroutines
Decided I will allow wildcards in paths. Hasn't been implemented though.
Fixed issues with ExtendedDirectory reading (I was using a uint16 instead of a uint32)
I couldn't test basically any of this. somehow a for loop isn't incrementing. For seemingly no reason.
This commit is contained in:
Caleb Gardner
2020-12-01 06:14:09 -06:00
parent 1254df2861
commit e302d98665
4 changed files with 130 additions and 48 deletions
+86 -29
View File
@@ -59,7 +59,6 @@ func (f *File) GetChildren() (children []*File, err error) {
} }
dir, err := f.r.readDirFromInode(f.in) dir, err := f.r.readDirFromInode(f.in)
if err != nil { if err != nil {
fmt.Println("err reading dir")
return return
} }
var fil *File var fil *File
@@ -86,26 +85,31 @@ func (f *File) GetChildrenRecursively() (children []*File, err error) {
if !f.IsDir() { if !f.IsDir() {
return nil, errNotDirectory return nil, errNotDirectory
} }
chil, err := f.GetChildren() children, err = f.GetChildren()
if err != nil { if err != nil {
fmt.Println("err here", f.Path())
return return
} }
var childFolders []*File var childFolders []*File
for _, child := range chil { for _, child := range children {
children = append(children, child)
if child.IsDir() { if child.IsDir() {
childFolders = append(childFolders, child) childFolders = append(childFolders, child)
} }
} }
foldChil := make(chan []*File)
errChan := make(chan error)
for _, folds := range childFolders { for _, folds := range childFolders {
var childs []*File go func(fil *File) {
childs, err = folds.GetChildrenRecursively() childs, err := fil.GetChildrenRecursively()
errChan <- err
foldChil <- childs
}(folds)
}
for range childFolders {
err = <-errChan
if err != nil { if err != nil {
fmt.Println("err here Recursive", folds.Path())
return return
} }
children = append(children, childs...) children = append(children, <-foldChil...)
} }
return return
} }
@@ -120,6 +124,7 @@ func (f *File) Path() string {
//GetFileAtPath tries to return the File at the given path, relative to the file. //GetFileAtPath tries to return the File at the given path, relative to the file.
//Returns nil if called on something other then a folder, OR if the path goes oustide the archive. //Returns nil if called on something other then a folder, OR if the path goes oustide the archive.
//Allows * wildcards.
func (f *File) GetFileAtPath(path string) *File { func (f *File) GetFileAtPath(path string) *File {
if path == "" { if path == "" {
return f return f
@@ -145,9 +150,10 @@ func (f *File) GetFileAtPath(path string) *File {
if err != nil { if err != nil {
return nil return nil
} }
for _, child := range children { for _, child := range children {
if child.Name == split[0] { if strings.Contains(split[0], "*") {
//TODO: wildcards
} else if child.Name == split[0] {
return child.GetFileAtPath(strings.Join(split[1:], "/")) return child.GetFileAtPath(strings.Join(split[1:], "/"))
} }
} }
@@ -229,18 +235,37 @@ func (f *File) ExtractWithOptions(path string, unbreakSymlink bool, folderPerm o
} }
switch { switch {
case f.IsDir(): case f.IsDir():
err = os.Mkdir(path+"/"+f.Name, f.Permission()) if f.Name != "" {
if err != nil { //TODO: check if folder is present, and if so, try to set it's permission
if verbose { err = os.Mkdir(path+"/"+f.Name, f.Permission())
fmt.Println("Error while making: "+path+"/"+f.Name, f.Permission()) if err != nil {
if verbose {
fmt.Println("Error while making: ", path+"/"+f.Name)
}
errs = append(errs, err)
return
}
fil, err := os.Open(path + "/" + f.Name)
if err != nil {
if verbose {
fmt.Println("Error while opening:", path+"/"+f.Name)
}
errs = append(errs, err)
return
}
err = fil.Chown(int(f.r.idTable[f.in.Header.UID]), int(f.r.idTable[f.in.Header.GID]))
if err != nil {
if verbose {
fmt.Println("Error while changing owner:", path+"/"+f.Name)
}
errs = append(errs, err)
return
} }
errs = append(errs, err)
return
} }
children, err := f.GetChildren() children, err := f.GetChildren()
if err != nil { if err != nil {
if verbose { if verbose {
fmt.Println("Error while making: "+path+"/"+f.Name, f.Permission()) fmt.Println("Error getting children for:", f.Path())
} }
errs = append(errs, err) errs = append(errs, err)
return return
@@ -249,7 +274,11 @@ func (f *File) ExtractWithOptions(path string, unbreakSymlink bool, folderPerm o
defer close(finishChan) defer close(finishChan)
for _, child := range children { for _, child := range children {
go func(child *File) { go func(child *File) {
finishChan <- child.ExtractWithOptions(path, unbreakSymlink, folderPerm, verbose) if f.Name == "" {
finishChan <- child.ExtractWithOptions(path, unbreakSymlink, folderPerm, verbose)
} else {
finishChan <- child.ExtractWithOptions(path+"/"+f.Name, unbreakSymlink, folderPerm, verbose)
}
}(child) }(child)
} }
for range children { for range children {
@@ -258,17 +287,43 @@ func (f *File) ExtractWithOptions(path string, unbreakSymlink bool, folderPerm o
return return
case f.IsFile(): case f.IsFile():
fil, err := os.Create(path + "/" + f.Name) fil, err := os.Create(path + "/" + f.Name)
if err != nil { if os.IsExist(err) {
err = os.Remove(path + "/" + f.Name)
if err != nil {
if verbose {
fmt.Println("Error while making:", path+"/"+f.Name)
}
errs = append(errs, err)
return
}
fil, err = os.Create(path + "/" + f.Name)
if err != nil {
if verbose {
fmt.Println("Error while making:", path+"/"+f.Name)
}
errs = append(errs, err)
return
}
} else if err != nil {
if verbose { if verbose {
fmt.Println("Error while making: "+path+"/"+f.Name, f.Permission()) fmt.Println("Error while making:", path+"/"+f.Name)
} }
errs = append(errs, err) errs = append(errs, err)
return return
} }
defer f.Close() //Since we will be reading from the file
_, err = io.Copy(fil, f) _, err = io.Copy(fil, f)
if err != nil { if err != nil {
if verbose { if verbose {
fmt.Println("Error while Copying data to: "+path+"/"+f.Name, f.Permission()) fmt.Println("Error while Copying data to:", path+"/"+f.Name)
}
errs = append(errs, err)
return
}
err = fil.Chown(int(f.r.idTable[f.in.Header.UID]), int(f.r.idTable[f.in.Header.GID]))
if err != nil {
if verbose {
fmt.Println("Error while changing owner:", path+"/"+f.Name)
} }
errs = append(errs, err) errs = append(errs, err)
return return
@@ -276,7 +331,7 @@ func (f *File) ExtractWithOptions(path string, unbreakSymlink bool, folderPerm o
err = fil.Chmod(f.Permission()) err = fil.Chmod(f.Permission())
if err != nil { if err != nil {
if verbose { if verbose {
fmt.Println("Error while setting permissions for: "+path+"/"+f.Name, f.Permission()) fmt.Println("Error while setting permissions for:", path+"/"+f.Name)
} }
errs = append(errs, err) errs = append(errs, err)
} }
@@ -293,10 +348,12 @@ func (f *File) Close() error {
if f.IsDir() { if f.IsDir() {
return errNotFile return errNotFile
} }
if closer, is := f.Reader.(io.Closer); is { if f.Reader != nil {
closer.Close() if closer, is := f.Reader.(io.Closer); is {
closer.Close()
}
f.Reader = nil
} }
f.Reader = nil
return nil return nil
} }
@@ -320,16 +377,16 @@ func (f *File) Read(p []byte) (int, error) {
func (r *Reader) readDirFromInode(i *inode.Inode) (*directory.Directory, error) { func (r *Reader) readDirFromInode(i *inode.Inode) (*directory.Directory, error) {
var offset uint32 var offset uint32
var metaOffset uint16 var metaOffset uint16
var size uint16 var size uint32
switch i.Type { switch i.Type {
case inode.BasicDirectoryType: case inode.BasicDirectoryType:
offset = i.Info.(inode.BasicDirectory).DirectoryIndex offset = i.Info.(inode.BasicDirectory).DirectoryIndex
metaOffset = i.Info.(inode.BasicDirectory).DirectoryOffset metaOffset = i.Info.(inode.BasicDirectory).DirectoryOffset
size = i.Info.(inode.BasicDirectory).DirectorySize size = uint32(i.Info.(inode.BasicDirectory).DirectorySize)
case inode.ExtDirType: case inode.ExtDirType:
offset = i.Info.(inode.ExtendedDirectory).Init.DirectoryIndex offset = i.Info.(inode.ExtendedDirectory).Init.DirectoryIndex
metaOffset = i.Info.(inode.ExtendedDirectory).Init.DirectoryOffset metaOffset = i.Info.(inode.ExtendedDirectory).Init.DirectoryOffset
size = uint16(i.Info.(inode.ExtendedDirectory).Init.DirectorySize) size = i.Info.(inode.ExtendedDirectory).Init.DirectorySize
default: default:
return nil, errors.New("Not a directory inode") return nil, errors.New("Not a directory inode")
} }
+1 -1
View File
@@ -52,7 +52,7 @@ type Directory struct {
} }
//NewDirectory reads the directory from rdr //NewDirectory reads the directory from rdr
func NewDirectory(base io.Reader, size uint16) (*Directory, error) { func NewDirectory(base io.Reader, size uint32) (*Directory, error) {
var dir Directory var dir Directory
var err error var err error
tmp := make([]byte, size) tmp := make([]byte, size)
+35 -1
View File
@@ -3,6 +3,7 @@ package squashfs
import ( import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt"
"io" "io"
"math" "math"
@@ -30,6 +31,7 @@ type Reader struct {
flags superblockFlags flags superblockFlags
decompressor compression.Decompressor decompressor compression.Decompressor
fragOffsets []uint64 fragOffsets []uint64
idTable []uint32
} }
//NewSquashfsReader returns a new squashfs.Reader from an io.ReaderAt //NewSquashfsReader returns a new squashfs.Reader from an io.ReaderAt
@@ -50,13 +52,14 @@ func NewSquashfsReader(r io.ReaderAt) (*Reader, error) {
case xzCompression: case xzCompression:
rdr.decompressor = &compression.Xz{} rdr.decompressor = &compression.Xz{}
default: default:
//TODO: all compression types.
return nil, errIncompatibleCompression return nil, errIncompatibleCompression
} }
if rdr.flags.CompressorOptions { if rdr.flags.CompressorOptions {
//TODO: parse compressor options //TODO: parse compressor options
return nil, errCompressorOptions return nil, errCompressorOptions
} }
fragBlocks := int(math.Ceil(float64(rdr.super.FragCount) / 512.0)) fragBlocks := int(math.Ceil(float64(rdr.super.FragCount) / 512))
if fragBlocks > 0 { if fragBlocks > 0 {
offset := int64(rdr.super.FragTableStart) offset := int64(rdr.super.FragTableStart)
for i := 0; i < fragBlocks; i++ { for i := 0; i < fragBlocks; i++ {
@@ -69,6 +72,37 @@ func NewSquashfsReader(r io.ReaderAt) (*Reader, error) {
offset += 8 offset += 8
} }
} }
idBlocks := int(math.Ceil(float64(rdr.super.IDCount) / 2048))
fmt.Println("ID Blocks", idBlocks)
for idBlocks > 0 {
unread := rdr.super.IDCount
offset := int64(rdr.super.IDTableStart)
for i := 0; i < idBlocks; i++ {
tmp := make([]byte, 8)
_, err = r.ReadAt(tmp, offset)
if err != nil {
return nil, err
}
offset += 8
idRdr, err := rdr.newMetadataReader(int64(binary.LittleEndian.Uint64(tmp)))
if err != nil {
return nil, err
}
for j := 0; j < int(math.Min(float64(unread), 2048)); j++ {
var id uint32
err = binary.Read(idRdr, binary.LittleEndian, &id)
if err != nil {
return nil, err
}
rdr.idTable = append(rdr.idTable, id)
}
if unread > 2048 {
unread -= 2048
} else {
unread = 0
}
}
}
return &rdr, nil return &rdr, nil
} }
+8 -17
View File
@@ -8,7 +8,6 @@ import (
"testing" "testing"
goappimage "github.com/CalebQ42/GoAppImage" goappimage "github.com/CalebQ42/GoAppImage"
"github.com/CalebQ42/squashfs/internal/inode"
) )
const ( const (
@@ -18,7 +17,7 @@ const (
) )
func TestSquashfs(t *testing.T) { func TestSquashfs(t *testing.T) {
t.Parallel() fmt.Println("YOOO")
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -31,21 +30,13 @@ func TestSquashfs(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
fils, err := rdr.GetAllFiles() os.RemoveAll(wd + "/testing/" + squashfsName + ".d")
if err != nil { fmt.Println("Whaaaa")
t.Fatal(err) root, _ := rdr.GetRootFolder()
} fmt.Println("WHYYYY")
for _, fil := range fils { errs := root.ExtractWithOptions(wd+"/testing/"+squashfsName+".d", false, os.ModePerm, true)
if fil.filType != inode.BasicFileType && fil.filType != inode.BasicDirectoryType && fil.filType != inode.BasicSymlinkType { fmt.Println(errs)
fmt.Println("Found non-standard") t.Fatal("No prolems here!")
fmt.Println(fil.Path())
fmt.Println("type:", fil.filType)
} else if fil.IsSymlink() {
fmt.Println("Symlink!")
fmt.Println(fil.Path())
fmt.Println("symlink path:", fil.SymlinkPath())
}
}
} }
func TestAppImage(t *testing.T) { func TestAppImage(t *testing.T) {