Symlink file support
You can get the path that the symlink is pointing to, AND get the squashfs.File for it. You can also now give a path FROM a given squashfs.File directory.
This commit is contained in:
@@ -1,43 +1,13 @@
|
||||
# GoSquashfs
|
||||
|
||||
[](https://pkg.go.dev/github.com/CalebQ42/GoSquashfs)
|
||||
# squashfs [](https://pkg.go.dev/github.com/CalebQ42/GoSquashfs)
|
||||
|
||||
A PURE Go library to read and write squashfs.
|
||||
|
||||
Currently, you can read a squashfs and extract files (only files at the moment). Many things are public that shouldn't be, but you can use it by using NewSquashfsReader and subsequent ReadFile.
|
||||
Currently, you can read a squashfs and extract files (folder extraction not supported. Yet).
|
||||
|
||||
Special thanks to https://dr-emann.github.io/squashfs/ for some VERY important information in an easy to understand format.
|
||||
Thanks also to [distri's squashfs library](https://github.com/distr1/distri/tree/master/internal/squashfs) as I referenced it to figure some things out (and double check others).
|
||||
|
||||
# Working
|
||||
|
||||
* Extracting files from string paths
|
||||
* Reading the header
|
||||
* Reading metadata blocks (whether encrypted or not)
|
||||
* Reading inodes
|
||||
* Reading directories
|
||||
* Basic gzip compression (Shouldn't be too hard to implement other, but for right now, this works)
|
||||
* Listing all files via a string slice
|
||||
|
||||
# Not Working (Yet). Not necessarily in order.
|
||||
|
||||
* Provide an easy interface to find and list files and their properties
|
||||
* Maybe squashfs.File
|
||||
* Make device, socket, symlink, and all extended types of inode work properly. (I need to find an archive that uses it first.)
|
||||
* Extracting files
|
||||
* from inodes.
|
||||
* from file info.
|
||||
* Give a list of files
|
||||
* In io.FileStat (?) form
|
||||
* Reading the UID, GUID, Xatt, Compression Options, and Export tables.
|
||||
* Implement other compression types (Should be relatively easy)
|
||||
* Squashing
|
||||
* Threading processes to speed them up
|
||||
* Reasonable tests
|
||||
|
||||
# TODO
|
||||
|
||||
* Go over all documentation again (especially for exported structs and functions) to make sure it's easy to understand.
|
||||
# [TODO](https://github.com/CalebQ42/squashfs/projects/1?fullscreen=true)
|
||||
|
||||
# Where I'm at
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package squashfs
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/CalebQ42/squashfs/internal/directory"
|
||||
"github.com/CalebQ42/squashfs/internal/inode"
|
||||
@@ -22,7 +23,7 @@ var (
|
||||
//When writing, this holds the information on WHERE the file will be placed inside the archive.
|
||||
type File struct {
|
||||
Name string //The name of the file or folder. Root folder will not have a name ("")
|
||||
Parent *File //The parent directory. If it's the root directory, will be nil
|
||||
Parent *File //The parent directory. Should ALWAYS be a folder. If it's the root directory, will be nil
|
||||
Reader io.Reader //Underlying reader. When writing, will probably be an os.File. When reading this is kept nil UNTIL reading to save memory.
|
||||
Path string //The path to the folder the File is located in.
|
||||
r *Reader //The squashfs.Reader where this file is contained.
|
||||
@@ -102,11 +103,66 @@ func (f *File) GetChildrenRecursively() (children []*File, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
//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.
|
||||
func (f *File) GetFileAtPath(path string) *File {
|
||||
if path == "" {
|
||||
return f
|
||||
}
|
||||
path = strings.TrimSuffix(strings.TrimPrefix(path, "/"), "/")
|
||||
if path != "" && !f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
for strings.HasSuffix(path, "./") {
|
||||
//since you can TECHNICALLY have an infinite amount of ./ and it would still be valid.
|
||||
path = strings.TrimPrefix(path, "./")
|
||||
}
|
||||
split := strings.Split(path, "/")
|
||||
children, err := f.GetChildren()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, child := range children {
|
||||
if child.Name == split[0] {
|
||||
return child.GetFileAtPath(strings.Join(split[1:], "/"))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//IsDir returns if the file is a directory.
|
||||
func (f *File) IsDir() bool {
|
||||
return f.filType == inode.BasicDirectoryType || f.filType == inode.ExtDirType
|
||||
}
|
||||
|
||||
//IsSymlink returns if the file is a symlink.
|
||||
func (f *File) IsSymlink() bool {
|
||||
return f.filType == inode.BasicSymlinkType || f.filType == inode.ExtSymlinkType
|
||||
}
|
||||
|
||||
//SymlinkPath returns the path the symlink is pointing to. If the file ISN'T a symlink, will return an empty string
|
||||
func (f *File) SymlinkPath() string {
|
||||
switch f.filType {
|
||||
case inode.BasicSymlinkType:
|
||||
return f.in.Info.(inode.BasicSymlink).Path
|
||||
case inode.ExtSymlinkType:
|
||||
return f.in.Info.(inode.ExtendedSymlink).Path
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
//GetSymlinkFile tries to return the squashfs.File associated with the symlink
|
||||
func (f *File) GetSymlinkFile() *File {
|
||||
if !f.IsSymlink() {
|
||||
return nil
|
||||
}
|
||||
if strings.HasSuffix(f.SymlinkPath(), "/") {
|
||||
return nil
|
||||
}
|
||||
return f.r.GetFileAtPath(f.SymlinkPath())
|
||||
}
|
||||
|
||||
//Close frees up the memory held up by the underlying reader. Should NOT be called when writing.
|
||||
//When reading, Close is safe to use, but any subsequent Read calls resets to the beginning of the file.
|
||||
func (f *File) Close() error {
|
||||
|
||||
@@ -181,6 +181,7 @@ type BasicSymlinkInit struct {
|
||||
type BasicSymlink struct {
|
||||
Init BasicSymlinkInit
|
||||
targetPath []byte //len is TargetPathSize
|
||||
Path string
|
||||
}
|
||||
|
||||
//NewBasicSymlink creates a new BasicSymlink
|
||||
@@ -192,6 +193,10 @@ func NewBasicSymlink(rdr io.Reader) (BasicSymlink, error) {
|
||||
}
|
||||
inode.targetPath = make([]byte, inode.Init.TargetPathSize, inode.Init.TargetPathSize)
|
||||
err = binary.Read(rdr, binary.LittleEndian, &inode.targetPath)
|
||||
if err != nil {
|
||||
return inode, err
|
||||
}
|
||||
inode.Path = string(inode.targetPath)
|
||||
return inode, err
|
||||
}
|
||||
|
||||
@@ -204,7 +209,8 @@ type ExtendedSymlinkInit struct {
|
||||
//ExtendedSymlink is a symlink with extra information
|
||||
type ExtendedSymlink struct {
|
||||
Init ExtendedSymlinkInit
|
||||
TargetPath []uint8
|
||||
targetPath []uint8
|
||||
Path string
|
||||
XattrIndex uint32
|
||||
}
|
||||
|
||||
@@ -215,7 +221,12 @@ func NewExtendedSymlink(rdr io.Reader) (ExtendedSymlink, error) {
|
||||
if err != nil {
|
||||
return inode, err
|
||||
}
|
||||
inode.TargetPath = make([]uint8, inode.Init.TargetPathSize, inode.Init.TargetPathSize)
|
||||
inode.targetPath = make([]uint8, inode.Init.TargetPathSize, inode.Init.TargetPathSize)
|
||||
err = binary.Read(rdr, binary.LittleEndian, &inode.targetPath)
|
||||
if err != nil {
|
||||
return inode, err
|
||||
}
|
||||
inode.Path = string(inode.targetPath)
|
||||
err = binary.Read(rdr, binary.LittleEndian, &inode.XattrIndex)
|
||||
return inode, err
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/CalebQ42/squashfs/internal/inode"
|
||||
)
|
||||
@@ -160,34 +159,9 @@ func (r *Reader) FindAll(query func(*File) bool) (all []*File) {
|
||||
|
||||
//GetFileAtPath will return the file at the given path. If the file cannot be found, will return nil.
|
||||
func (r *Reader) GetFileAtPath(path string) *File {
|
||||
path = strings.TrimSuffix(strings.TrimPrefix(path, "/"), "/")
|
||||
pathDirs := strings.Split(path, "/")
|
||||
dir, err := r.GetRootFolder()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
children, err := dir.GetChildren()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, folder := range pathDirs {
|
||||
for _, child := range children {
|
||||
if child.Name == folder {
|
||||
dir = child
|
||||
if dir.IsDir() {
|
||||
children, err = dir.GetChildren()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
children = make([]*File, 0)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if dir.Path+"/"+dir.Name == "/"+path {
|
||||
return dir
|
||||
}
|
||||
return nil
|
||||
return dir.GetFileAtPath(path)
|
||||
}
|
||||
|
||||
+7
-27
@@ -5,15 +5,14 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
goappimage "github.com/CalebQ42/GoAppImage"
|
||||
)
|
||||
|
||||
const (
|
||||
downloadURL = "https://github.com/zilti/code-oss.AppImage/releases/download/continuous/Code_OSS-x86_64.AppImage"
|
||||
appImageName = "Code_OSS.AppImage"
|
||||
downloadURL = "https://github.com/Swordfish90/cool-retro-term/releases/download/1.1.1/Cool-Retro-Term-1.1.1-x86_64.AppImage"
|
||||
appImageName = "Cool-Retro-Term.AppImage"
|
||||
)
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
@@ -39,31 +38,12 @@ func TestMain(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rdr.FindAll(func(fil *File) bool {
|
||||
return strings.HasSuffix(fil.Name, ".desktop")
|
||||
})
|
||||
fils, err := rdr.GetAllFiles()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
fil := rdr.GetFileAtPath("/usr/bin/cool-retro-term")
|
||||
if fil != nil {
|
||||
fmt.Println("Worked!", fil.Path+"/"+fil.Name)
|
||||
} else {
|
||||
t.Fatal("NOOOOOO!")
|
||||
}
|
||||
for _, fil := range fils {
|
||||
fmt.Println(fil.Path + "/" + fil.Name)
|
||||
}
|
||||
// extractionFil := "code-oss.desktop"
|
||||
// os.Remove(wd + "/testing/" + extractionFil)
|
||||
// desk, err := os.Create(wd + "/testing/" + extractionFil)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// ext := rdr.GetFileAtPath(extractionFil)
|
||||
// if ext == nil {
|
||||
// t.Fatal("Cannot find file")
|
||||
// }
|
||||
// defer ext.Close()
|
||||
// _, err = io.Copy(desk, ext)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
t.Fatal("No problems here!")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user