You can lookup an inode via a string path now
Removed DecompressCopy (as I wasn't using it) Started work on fragments
This commit is contained in:
@@ -15,6 +15,7 @@ Thanks also to [distri's squashfs library](https://github.com/distr1/distri/tree
|
|||||||
* Reading inodes
|
* Reading inodes
|
||||||
* Reading directories
|
* Reading directories
|
||||||
* Basic gzip compression (Shouldn't be too hard to implement other, but for right now, this works)
|
* 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). Roughly in order.
|
# Not Working (Yet). Roughly in order.
|
||||||
|
|
||||||
@@ -31,4 +32,4 @@ Thanks also to [distri's squashfs library](https://github.com/distr1/distri/tree
|
|||||||
|
|
||||||
# Where I'm at.
|
# Where I'm at.
|
||||||
|
|
||||||
* I CAN READ THE ENTIRE DIRECTORY!!!!! (This is a big ol' step)
|
* Started work on fragments
|
||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
type Decompressor interface {
|
type Decompressor interface {
|
||||||
Decompress(io.Reader) ([]byte, error)
|
Decompress(io.Reader) ([]byte, error)
|
||||||
DecompressCopy(*io.Writer, *io.SectionReader) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ZlibDecompressor struct{}
|
type ZlibDecompressor struct{}
|
||||||
@@ -25,15 +24,3 @@ func (z *ZlibDecompressor) Decompress(r io.Reader) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return data.Bytes(), nil
|
return data.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (z *ZlibDecompressor) DecompressCopy(w *io.Writer, r *io.SectionReader) error {
|
|
||||||
rdr, err := zlib.NewReader(r)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = io.Copy(*w, rdr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package squashfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/CalebQ42/GoSquashfs/internal/inode"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FragmentEntryRaw struct {
|
||||||
|
Start uint64
|
||||||
|
Size uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type FragmentEntry struct {
|
||||||
|
start uint64
|
||||||
|
size uint32
|
||||||
|
compressed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewFragmentEntry reads a fragment entry from the given io.Reader.
|
||||||
|
func (r *Reader) NewFragmentEntry(rdr *io.Reader) (*FragmentEntry, error) {
|
||||||
|
var entry FragmentEntry
|
||||||
|
var raw FragmentEntryRaw
|
||||||
|
err := binary.Read(*rdr, binary.LittleEndian, &raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
entry.start = raw.Start
|
||||||
|
entry.compressed = raw.Size&0x1000000 == 0x1000000
|
||||||
|
entry.size = raw.Size &^ 0x1000000
|
||||||
|
return &entry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetFragmentFromInode returns the fragment data for a given inode
|
||||||
|
func (r *Reader) GetFragmentFromInode(in *inode.Inode) ([]byte, error) {
|
||||||
|
if in.Type != inode.BasicFileType {
|
||||||
|
return nil, errors.New("Only basic file is supported right now")
|
||||||
|
}
|
||||||
|
bf := in.Info.(inode.BasicFile)
|
||||||
|
var size uint32
|
||||||
|
if bf.Init.BlockStart == 0 {
|
||||||
|
size = bf.Init.Size
|
||||||
|
} else {
|
||||||
|
size = bf.BlockSizes
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-17
@@ -15,11 +15,11 @@ type Inode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//ProcessInode tries to read an inode from the BlockReader
|
//ProcessInode tries to read an inode from the BlockReader
|
||||||
func ProcessInode(br io.Reader, blockSize uint32) (Inode, error) {
|
func ProcessInode(br io.Reader, blockSize uint32) (*Inode, error) {
|
||||||
var head Header
|
var head Header
|
||||||
err := binary.Read(br, binary.LittleEndian, &head)
|
err := binary.Read(br, binary.LittleEndian, &head)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var info interface{}
|
var info interface{}
|
||||||
switch head.InodeType {
|
switch head.InodeType {
|
||||||
@@ -27,97 +27,97 @@ func ProcessInode(br io.Reader, blockSize uint32) (Inode, error) {
|
|||||||
var inode BasicDirectory
|
var inode BasicDirectory
|
||||||
err = binary.Read(br, binary.LittleEndian, &inode)
|
err = binary.Read(br, binary.LittleEndian, &inode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case BasicFileType:
|
case BasicFileType:
|
||||||
inode, err := NewBasicFile(br, blockSize)
|
inode, err := NewBasicFile(br, blockSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case BasicSymlinkType:
|
case BasicSymlinkType:
|
||||||
inode, err := NewBasicSymlink(br)
|
inode, err := NewBasicSymlink(br)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case BasicBlockDeviceType:
|
case BasicBlockDeviceType:
|
||||||
var inode BasicDevice
|
var inode BasicDevice
|
||||||
err = binary.Read(br, binary.LittleEndian, &inode)
|
err = binary.Read(br, binary.LittleEndian, &inode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case BasicCharDeviceType:
|
case BasicCharDeviceType:
|
||||||
var inode BasicDevice
|
var inode BasicDevice
|
||||||
err = binary.Read(br, binary.LittleEndian, &inode)
|
err = binary.Read(br, binary.LittleEndian, &inode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case BasicFifoType:
|
case BasicFifoType:
|
||||||
var inode BasicIPC
|
var inode BasicIPC
|
||||||
err = binary.Read(br, binary.LittleEndian, &inode)
|
err = binary.Read(br, binary.LittleEndian, &inode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case BasicSocketType:
|
case BasicSocketType:
|
||||||
var inode BasicIPC
|
var inode BasicIPC
|
||||||
err = binary.Read(br, binary.LittleEndian, &inode)
|
err = binary.Read(br, binary.LittleEndian, &inode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case ExtDirType:
|
case ExtDirType:
|
||||||
inode, err := NewExtendedDirectory(br)
|
inode, err := NewExtendedDirectory(br)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case ExtFileType:
|
case ExtFileType:
|
||||||
inode, err := NewExtendedFile(br, blockSize)
|
inode, err := NewExtendedFile(br, blockSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case ExtSymlinkType:
|
case ExtSymlinkType:
|
||||||
inode, err := NewExtendedSymlink(br)
|
inode, err := NewExtendedSymlink(br)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case ExtBlockDeviceType:
|
case ExtBlockDeviceType:
|
||||||
var inode ExtendedDevice
|
var inode ExtendedDevice
|
||||||
err = binary.Read(br, binary.LittleEndian, &inode)
|
err = binary.Read(br, binary.LittleEndian, &inode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case ExtCharDeviceType:
|
case ExtCharDeviceType:
|
||||||
var inode ExtendedDevice
|
var inode ExtendedDevice
|
||||||
err = binary.Read(br, binary.LittleEndian, &inode)
|
err = binary.Read(br, binary.LittleEndian, &inode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case ExtFifoType:
|
case ExtFifoType:
|
||||||
var inode ExtendedIPC
|
var inode ExtendedIPC
|
||||||
err = binary.Read(br, binary.LittleEndian, &inode)
|
err = binary.Read(br, binary.LittleEndian, &inode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
case ExtSocketType:
|
case ExtSocketType:
|
||||||
var inode ExtendedIPC
|
var inode ExtendedIPC
|
||||||
err = binary.Read(br, binary.LittleEndian, &inode)
|
err = binary.Read(br, binary.LittleEndian, &inode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Inode{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info = inode
|
info = inode
|
||||||
}
|
}
|
||||||
return Inode{
|
return &Inode{
|
||||||
Type: int(head.InodeType),
|
Type: int(head.InodeType),
|
||||||
Header: head,
|
Header: head,
|
||||||
Info: info,
|
Info: info,
|
||||||
|
|||||||
+38
-17
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/CalebQ42/GoSquashfs/internal/directory"
|
"github.com/CalebQ42/GoSquashfs/internal/directory"
|
||||||
"github.com/CalebQ42/GoSquashfs/internal/inode"
|
"github.com/CalebQ42/GoSquashfs/internal/inode"
|
||||||
@@ -14,6 +15,17 @@ const (
|
|||||||
magic = 0x73717368
|
magic = 0x73717368
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//ErrNoMagic is returned if the magic number in the superblock isn't correct.
|
||||||
|
ErrNoMagic = errors.New("Magic number doesn't match. Either isn't a squashfs or corrupted")
|
||||||
|
//ErrIncompatibleCompression is returned if the compression type in the superblock doesn't work.
|
||||||
|
ErrIncompatibleCompression = errors.New("Compression type unsupported")
|
||||||
|
//ErrCompressorOptions is returned if compressor options is present. It's not currently supported.
|
||||||
|
ErrCompressorOptions = errors.New("Compressor options is not currently supported")
|
||||||
|
)
|
||||||
|
|
||||||
|
//Reader processes and reads a squashfs archive.
|
||||||
|
//TODO: Give a way to actually read files :P
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
r io.ReaderAt
|
r io.ReaderAt
|
||||||
super Superblock
|
super Superblock
|
||||||
@@ -22,6 +34,7 @@ type Reader struct {
|
|||||||
dirs []*directory.Directory
|
dirs []*directory.Directory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewSquashfsReader returns a new squashfs.Reader from an io.ReaderAt
|
||||||
func NewSquashfsReader(r io.ReaderAt) (*Reader, error) {
|
func NewSquashfsReader(r io.ReaderAt) (*Reader, error) {
|
||||||
var rdr Reader
|
var rdr Reader
|
||||||
rdr.r = r
|
rdr.r = r
|
||||||
@@ -30,25 +43,43 @@ func NewSquashfsReader(r io.ReaderAt) (*Reader, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rdr.super.Magic != magic {
|
if rdr.super.Magic != magic {
|
||||||
return nil, errors.New("doesn't have magic number, probably isn't a squashfs")
|
return nil, ErrNoMagic
|
||||||
}
|
}
|
||||||
rdr.flags = rdr.super.GetFlags()
|
rdr.flags = rdr.super.GetFlags()
|
||||||
switch rdr.super.CompressionType {
|
switch rdr.super.CompressionType {
|
||||||
case gzipCompression:
|
case gzipCompression:
|
||||||
rdr.decompressor = &ZlibDecompressor{}
|
rdr.decompressor = &ZlibDecompressor{}
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("Unsupported compression type")
|
return nil, ErrIncompatibleCompression
|
||||||
}
|
}
|
||||||
if rdr.flags.CompressorOptions {
|
if rdr.flags.CompressorOptions {
|
||||||
//TODO: parse compressor options
|
//TODO: parse compressor options
|
||||||
fmt.Println("Compressor options is NOT currently supported")
|
return nil, ErrCompressorOptions
|
||||||
return nil, errors.New("Has compressor options")
|
|
||||||
}
|
}
|
||||||
return &rdr, nil
|
return &rdr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//GetFilesList returns a list of ALL files in the squashfs, going down every folder.
|
||||||
|
//Paths that terminate in a folder end with /
|
||||||
|
func (r *Reader) GetFilesList() ([]string, error) {
|
||||||
|
inoderdr, err := r.NewBlockReaderFromInodeRef(r.super.RootInodeRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
i, err := inode.ProcessInode(inoderdr, r.super.BlockSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paths, err := r.readDir(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return paths, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//readDir returns a list of all decendents of a given inode. Inode given MUST be a directory type.
|
||||||
func (r *Reader) readDir(i *inode.Inode) (paths []string, err error) {
|
func (r *Reader) readDir(i *inode.Inode) (paths []string, err error) {
|
||||||
dir, err := r.ReadDirFromInode(*i)
|
dir, err := r.ReadDirFromInode(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -77,20 +108,10 @@ func (r *Reader) readDir(i *inode.Inode) (paths []string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) readDirTable() error {
|
func (r *Reader) readDirTable() error {
|
||||||
inoderdr, err := r.NewBlockReaderFromInodeRef(r.super.RootInodeRef)
|
paths, err := r.GetFilesList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
i, err := inode.ProcessInode(inoderdr, r.super.BlockSize)
|
fmt.Println(strings.Join(paths, "\n"))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
paths, err := r.readDir(&i)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, path := range paths {
|
|
||||||
fmt.Println(path)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,17 @@ package squashfs
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/CalebQ42/GoSquashfs/internal/directory"
|
"github.com/CalebQ42/GoSquashfs/internal/directory"
|
||||||
"github.com/CalebQ42/GoSquashfs/internal/inode"
|
"github.com/CalebQ42/GoSquashfs/internal/inode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//ErrNotFound means that the given path is NOT present in the archive
|
||||||
|
ErrNotFound = errors.New("Path not found")
|
||||||
|
)
|
||||||
|
|
||||||
//ProcessInodeRef processes an inode reference and returns two values
|
//ProcessInodeRef processes an inode reference and returns two values
|
||||||
//
|
//
|
||||||
//The first value is the inode table offset. AKA, it's where the metadata block of the inode STARTS relative to the inode table.
|
//The first value is the inode table offset. AKA, it's where the metadata block of the inode STARTS relative to the inode table.
|
||||||
@@ -19,7 +25,9 @@ func processInodeRef(inodeRef uint64) (tableOffset uint64, metaOffset uint64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) ReadDirFromInode(i inode.Inode) (*directory.Directory, error) {
|
//ReadDirFromInode returns a fully populated directory.Directory from a given inode.Inode.
|
||||||
|
//If the given inode is not a directory it returns an 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 uint16
|
||||||
@@ -50,6 +58,7 @@ func (r *Reader) ReadDirFromInode(i inode.Inode) (*directory.Directory, error) {
|
|||||||
return dir, nil
|
return dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//GetInodeFromEntry returns the inode associated with a given directory.Entry
|
||||||
func (r *Reader) GetInodeFromEntry(en *directory.Entry) (*inode.Inode, error) {
|
func (r *Reader) GetInodeFromEntry(en *directory.Entry) (*inode.Inode, error) {
|
||||||
br, err := r.NewBlockReader(int64(r.super.InodeTableStart + uint64(en.Header.InodeOffset)))
|
br, err := r.NewBlockReader(int64(r.super.InodeTableStart + uint64(en.Header.InodeOffset)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -63,5 +72,45 @@ func (r *Reader) GetInodeFromEntry(en *directory.Entry) (*inode.Inode, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &i, nil
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetInodeFromPath returns the inode at the given path, relative to root.
|
||||||
|
//The given path can start or without "/".
|
||||||
|
func (r *Reader) GetInodeFromPath(path string) (*inode.Inode, error) {
|
||||||
|
path = strings.TrimSuffix(strings.TrimPrefix(path, "/"), "/")
|
||||||
|
pathDirs := strings.Split(path, "/")
|
||||||
|
rdr, err := r.NewBlockReaderFromInodeRef(r.super.RootInodeRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
curInodeDir, err := inode.ProcessInode(rdr, r.super.BlockSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for depth := 0; depth < len(pathDirs); depth++ {
|
||||||
|
if curInodeDir.Type != inode.BasicDirectoryType && curInodeDir.Type != inode.ExtDirType {
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
dir, err := r.ReadDirFromInode(curInodeDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, entry := range dir.Entries {
|
||||||
|
if entry.Name == pathDirs[depth] {
|
||||||
|
if depth == len(pathDirs)-1 {
|
||||||
|
in, err := r.GetInodeFromEntry(&entry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
|
curInodeDir, err = r.GetInodeFromEntry(&entry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user