Reorganization.
This commit is contained in:
@@ -1,33 +0,0 @@
|
|||||||
package squashfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"compress/zlib"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
//Decompressor is a squashfs decompressor interface. Allows for easy decompression no matter the type of compression.
|
|
||||||
type decompressor interface {
|
|
||||||
Decompress(io.Reader) ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type compressor interface {
|
|
||||||
Compress(io.Reader) ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
//ZlibDecompressor is a decompressor for gzip type compression
|
|
||||||
type zlibDecompressor struct{}
|
|
||||||
|
|
||||||
//Decompress reads the entirety of the given reader and returns it uncompressed as a byte slice.
|
|
||||||
func (z *zlibDecompressor) Decompress(r io.Reader) ([]byte, error) {
|
|
||||||
rdr, err := zlib.NewReader(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var data bytes.Buffer
|
|
||||||
_, err = io.Copy(&data, rdr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data.Bytes(), nil
|
|
||||||
}
|
|
||||||
@@ -11,11 +11,11 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
//ErrNotDirectory is returned when you're trying to do directory things with a non-directory
|
//ErrNotDirectory is returned when you're trying to do directory things with a non-directory
|
||||||
ErrNotDirectory = errors.New("File is not a directory")
|
errNotDirectory = errors.New("File is not a directory")
|
||||||
//ErrNotFile is returned when you're trying to do file things with a directory
|
//ErrNotFile is returned when you're trying to do file things with a directory
|
||||||
ErrNotFile = errors.New("File is not a file")
|
errNotFile = errors.New("File is not a file")
|
||||||
//ErrNotReading is returned when running functions that are only meant to be used when reading a squashfs
|
//ErrNotReading is returned when running functions that are only meant to be used when reading a squashfs
|
||||||
ErrNotReading = errors.New("Function only supported when reading a squashfs")
|
errNotReading = errors.New("Function only supported when reading a squashfs")
|
||||||
)
|
)
|
||||||
|
|
||||||
//File is the main way to interact with files within squashfs, or when putting files into a squashfs.
|
//File is the main way to interact with files within squashfs, or when putting files into a squashfs.
|
||||||
@@ -48,10 +48,10 @@ func (r *Reader) newFileFromDirEntry(entry *directory.Entry) (fil *File, err err
|
|||||||
func (f *File) GetChildren() (children []*File, err error) {
|
func (f *File) GetChildren() (children []*File, err error) {
|
||||||
children = make([]*File, 0)
|
children = make([]*File, 0)
|
||||||
if f.r == nil {
|
if f.r == nil {
|
||||||
return nil, ErrNotReading
|
return nil, errNotReading
|
||||||
}
|
}
|
||||||
if !f.IsDir() {
|
if !f.IsDir() {
|
||||||
return nil, ErrNotDirectory
|
return nil, errNotDirectory
|
||||||
}
|
}
|
||||||
dir, err := f.r.readDirFromInode(f.in)
|
dir, err := f.r.readDirFromInode(f.in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -76,10 +76,10 @@ func (f *File) GetChildren() (children []*File, err error) {
|
|||||||
func (f *File) GetChildrenRecursively() (children []*File, err error) {
|
func (f *File) GetChildrenRecursively() (children []*File, err error) {
|
||||||
children = make([]*File, 0)
|
children = make([]*File, 0)
|
||||||
if f.r == nil {
|
if f.r == nil {
|
||||||
return nil, ErrNotReading
|
return nil, errNotReading
|
||||||
}
|
}
|
||||||
if !f.IsDir() {
|
if !f.IsDir() {
|
||||||
return nil, ErrNotDirectory
|
return nil, errNotDirectory
|
||||||
}
|
}
|
||||||
chil, err := f.GetChildren()
|
chil, err := f.GetChildren()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -176,7 +176,7 @@ func (f *File) GetSymlinkFile() *File {
|
|||||||
//When reading, Close is safe to use, but any subsequent Read calls resets to the beginning of the file.
|
//When reading, Close is safe to use, but any subsequent Read calls resets to the beginning of the file.
|
||||||
func (f *File) Close() error {
|
func (f *File) Close() error {
|
||||||
if f.IsDir() {
|
if f.IsDir() {
|
||||||
return ErrNotFile
|
return errNotFile
|
||||||
}
|
}
|
||||||
if closer, is := f.Reader.(io.Closer); is {
|
if closer, is := f.Reader.(io.Closer); is {
|
||||||
closer.Close()
|
closer.Close()
|
||||||
@@ -199,3 +199,53 @@ func (f *File) Read(p []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
return f.Reader.Read(p)
|
return f.Reader.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//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 metaOffset uint16
|
||||||
|
var size uint16
|
||||||
|
switch i.Type {
|
||||||
|
case inode.BasicDirectoryType:
|
||||||
|
offset = i.Info.(inode.BasicDirectory).DirectoryIndex
|
||||||
|
metaOffset = i.Info.(inode.BasicDirectory).DirectoryOffset
|
||||||
|
size = i.Info.(inode.BasicDirectory).DirectorySize
|
||||||
|
case inode.ExtDirType:
|
||||||
|
offset = i.Info.(inode.ExtendedDirectory).Init.DirectoryIndex
|
||||||
|
metaOffset = i.Info.(inode.ExtendedDirectory).Init.DirectoryOffset
|
||||||
|
size = uint16(i.Info.(inode.ExtendedDirectory).Init.DirectorySize)
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Not a directory inode")
|
||||||
|
}
|
||||||
|
br, err := r.newMetadataReader(int64(r.super.DirTableStart + uint64(offset)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = br.Seek(int64(metaOffset), io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dir, err := directory.NewDirectory(br, size)
|
||||||
|
if err != nil {
|
||||||
|
return dir, err
|
||||||
|
}
|
||||||
|
return dir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetInodeFromEntry returns the inode associated with a given directory.Entry
|
||||||
|
func (r *Reader) getInodeFromEntry(en *directory.Entry) (*inode.Inode, error) {
|
||||||
|
br, err := r.newMetadataReader(int64(r.super.InodeTableStart + uint64(en.Header.InodeOffset)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = br.Seek(int64(en.Init.Offset), io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
i, err := inode.ProcessInode(br, r.super.BlockSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|||||||
+2
-2
@@ -22,7 +22,7 @@ type fileReader struct {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
//ErrPathIsNotFile returns when trying to read from a file, but the given path is NOT a file.
|
//ErrPathIsNotFile returns when trying to read from a file, but the given path is NOT a file.
|
||||||
ErrPathIsNotFile = errors.New("The given path is not a file")
|
errPathIsNotFile = errors.New("The given path is not a file")
|
||||||
)
|
)
|
||||||
|
|
||||||
//ReadFile provides a squashfs.FileReader for the file at the given location.
|
//ReadFile provides a squashfs.FileReader for the file at the given location.
|
||||||
@@ -30,7 +30,7 @@ func (r *Reader) newFileReader(in *inode.Inode) (*fileReader, error) {
|
|||||||
var rdr fileReader
|
var rdr fileReader
|
||||||
rdr.in = in
|
rdr.in = in
|
||||||
if in.Type != inode.BasicFileType && in.Type != inode.ExtFileType {
|
if in.Type != inode.BasicFileType && in.Type != inode.ExtFileType {
|
||||||
return nil, ErrPathIsNotFile
|
return nil, errPathIsNotFile
|
||||||
}
|
}
|
||||||
switch in.Type {
|
switch in.Type {
|
||||||
case inode.BasicFileType:
|
case inode.BasicFileType:
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package compression
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
//Compressor is a squashfs decompressor interface. Allows for easy compression.
|
||||||
|
type Compressor interface {
|
||||||
|
Compress(io.Reader) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Decompressor is a squashfs decompressor interface. Allows for easy decompression no matter the type of compression.
|
||||||
|
type Decompressor interface {
|
||||||
|
Decompress(io.Reader) ([]byte, error)
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package compression
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/zlib"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Zlib is a decompressor for gzip type compression
|
||||||
|
type Zlib struct{}
|
||||||
|
|
||||||
|
//Decompress reads the entirety of the given reader and returns it uncompressed as a byte slice.
|
||||||
|
func (z *Zlib) Decompress(r io.Reader) ([]byte, error) {
|
||||||
|
rdr, err := zlib.NewReader(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var data bytes.Buffer
|
||||||
|
_, err = io.Copy(&data, rdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data.Bytes(), nil
|
||||||
|
}
|
||||||
@@ -52,6 +52,17 @@ func (s *Reader) newMetadataReaderFromInodeRef(ref uint64) (*metadataReader, err
|
|||||||
return br, nil
|
return br, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//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 second value is the offset of the inode, INSIDE of the metadata.
|
||||||
|
func processInodeRef(inodeRef uint64) (tableOffset uint64, metaOffset uint64) {
|
||||||
|
tableOffset = inodeRef >> 16
|
||||||
|
metaOffset = inodeRef &^ 0xFFFFFFFF0000
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (br *metadataReader) parseMetadata() error {
|
func (br *metadataReader) parseMetadata() error {
|
||||||
var raw uint16
|
var raw uint16
|
||||||
err := binary.Read(io.NewSectionReader(br.s.r, br.offset, 2), binary.LittleEndian, &raw)
|
err := binary.Read(io.NewSectionReader(br.s.r, br.offset, 2), binary.LittleEndian, &raw)
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
|
"github.com/CalebQ42/squashfs/internal/compression"
|
||||||
"github.com/CalebQ42/squashfs/internal/inode"
|
"github.com/CalebQ42/squashfs/internal/inode"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,11 +16,11 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
//ErrNoMagic is returned if the magic number in the superblock isn't correct.
|
//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")
|
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 is returned if the compression type in the superblock doesn't work.
|
||||||
ErrIncompatibleCompression = errors.New("Compression type unsupported")
|
errIncompatibleCompression = errors.New("Compression type unsupported")
|
||||||
//ErrCompressorOptions is returned if compressor options is present. It's not currently supported.
|
//ErrCompressorOptions is returned if compressor options is present. It's not currently supported.
|
||||||
ErrCompressorOptions = errors.New("Compressor options is not currently supported")
|
errCompressorOptions = errors.New("Compressor options is not currently supported")
|
||||||
//ErrFragmentTableIssues is returned if there's trouble reading the fragment table when creating a reader.
|
//ErrFragmentTableIssues is returned if there's trouble reading the fragment table when creating a reader.
|
||||||
//When this is returned, the reader is still returned.
|
//When this is returned, the reader is still returned.
|
||||||
errFragmentTableIssues = errors.New("Trouble while reading the fragment table")
|
errFragmentTableIssues = errors.New("Trouble while reading the fragment table")
|
||||||
@@ -31,7 +32,7 @@ type Reader struct {
|
|||||||
r io.ReaderAt
|
r io.ReaderAt
|
||||||
super superblock
|
super superblock
|
||||||
flags superblockFlags
|
flags superblockFlags
|
||||||
decompressor decompressor
|
decompressor compression.Decompressor
|
||||||
fragOffsets []uint64
|
fragOffsets []uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,18 +45,18 @@ 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, ErrNoMagic
|
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 = &compression.Zlib{}
|
||||||
default:
|
default:
|
||||||
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.0))
|
||||||
if fragBlocks > 0 {
|
if fragBlocks > 0 {
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
package squashfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/CalebQ42/squashfs/internal/directory"
|
|
||||||
"github.com/CalebQ42/squashfs/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
|
|
||||||
//
|
|
||||||
//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 second value is the offset of the inode, INSIDE of the metadata.
|
|
||||||
func processInodeRef(inodeRef uint64) (tableOffset uint64, metaOffset uint64) {
|
|
||||||
tableOffset = inodeRef >> 16
|
|
||||||
metaOffset = inodeRef &^ 0xFFFFFFFF0000
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//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 metaOffset uint16
|
|
||||||
var size uint16
|
|
||||||
switch i.Type {
|
|
||||||
case inode.BasicDirectoryType:
|
|
||||||
offset = i.Info.(inode.BasicDirectory).DirectoryIndex
|
|
||||||
metaOffset = i.Info.(inode.BasicDirectory).DirectoryOffset
|
|
||||||
size = i.Info.(inode.BasicDirectory).DirectorySize
|
|
||||||
case inode.ExtDirType:
|
|
||||||
offset = i.Info.(inode.ExtendedDirectory).Init.DirectoryIndex
|
|
||||||
metaOffset = i.Info.(inode.ExtendedDirectory).Init.DirectoryOffset
|
|
||||||
size = uint16(i.Info.(inode.ExtendedDirectory).Init.DirectorySize)
|
|
||||||
default:
|
|
||||||
return nil, errors.New("Not a directory inode")
|
|
||||||
}
|
|
||||||
br, err := r.newMetadataReader(int64(r.super.DirTableStart + uint64(offset)))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = br.Seek(int64(metaOffset), io.SeekStart)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dir, err := directory.NewDirectory(br, size)
|
|
||||||
if err != nil {
|
|
||||||
return dir, err
|
|
||||||
}
|
|
||||||
return dir, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//GetInodeFromEntry returns the inode associated with a given directory.Entry
|
|
||||||
func (r *Reader) getInodeFromEntry(en *directory.Entry) (*inode.Inode, error) {
|
|
||||||
br, err := r.newMetadataReader(int64(r.super.InodeTableStart + uint64(en.Header.InodeOffset)))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = br.Seek(int64(en.Init.Offset), io.SeekStart)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
i, err := inode.ProcessInode(br, r.super.BlockSize)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//GetInodeFromPath returns the inode at the given path, relative to root.
|
|
||||||
//The given path can start with or without "/"
|
|
||||||
func (r *Reader) getInodeFromPath(path string) (*inode.Inode, error) {
|
|
||||||
path = strings.TrimSuffix(strings.TrimPrefix(path, "/"), "/")
|
|
||||||
pathDirs := strings.Split(path, "/")
|
|
||||||
rdr, err := r.newMetadataReaderFromInodeRef(r.super.RootInodeRef)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
curInodeDir, err := inode.ProcessInode(rdr, r.super.BlockSize)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if path == "" {
|
|
||||||
return curInodeDir, nil
|
|
||||||
}
|
|
||||||
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