232 lines
6.1 KiB
Go
232 lines
6.1 KiB
Go
package squashfs
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"io"
|
|
"math"
|
|
"time"
|
|
|
|
"github.com/CalebQ42/fuse"
|
|
"github.com/CalebQ42/squashfs/internal/decompress"
|
|
"github.com/CalebQ42/squashfs/internal/directory"
|
|
"github.com/CalebQ42/squashfs/internal/inode"
|
|
"github.com/CalebQ42/squashfs/internal/metadata"
|
|
"github.com/CalebQ42/squashfs/internal/toreader"
|
|
)
|
|
|
|
type Reader struct {
|
|
*FS
|
|
con *fuse.Conn
|
|
mountDone chan struct{}
|
|
d decompress.Decompressor
|
|
r io.ReaderAt
|
|
fragEntries []fragEntry
|
|
ids []uint32
|
|
// exportTable []uint64
|
|
s superblock
|
|
}
|
|
|
|
var (
|
|
ErrorMagic = errors.New("magic incorrect. probably not reading squashfs archive")
|
|
ErrorLog = errors.New("block log is incorrect. possible corrupted archive")
|
|
ErrorVersion = errors.New("squashfs version of archive is not 4.0")
|
|
)
|
|
|
|
// The types of compression supported by squashfs
|
|
const (
|
|
GZipCompression = uint16(iota + 1)
|
|
LZMACompression
|
|
LZOCompression
|
|
XZCompression
|
|
LZ4Compression
|
|
ZSTDCompression
|
|
)
|
|
|
|
func NewReaderAtOffset(r io.ReaderAt, off int64) (*Reader, error) {
|
|
return NewReader(toreader.NewOffsetReader(r, off))
|
|
}
|
|
|
|
// Creates a new squashfs.Reader from the given io.Reader. NOTE: All data from the io.Reader will be read and stored in memory.
|
|
func NewReaderFromReader(r io.Reader) (*Reader, error) {
|
|
rdr, err := toreader.NewReaderAt(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return NewReader(rdr)
|
|
}
|
|
|
|
// Creates a new squashfs.Reader from the given io.ReaderAt.
|
|
func NewReader(r io.ReaderAt) (*Reader, error) {
|
|
var squash Reader
|
|
squash.r = r
|
|
err := binary.Read(toreader.NewReader(r, 0), binary.LittleEndian, &squash.s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !squash.s.checkMagic() {
|
|
return nil, ErrorMagic
|
|
}
|
|
if !squash.s.checkBlockLog() {
|
|
return nil, ErrorLog
|
|
}
|
|
if !squash.s.checkVersion() {
|
|
return nil, ErrorVersion
|
|
}
|
|
switch squash.s.CompType {
|
|
case GZipCompression:
|
|
squash.d = decompress.GZip{}
|
|
case LZMACompression:
|
|
squash.d = decompress.Lzma{}
|
|
case LZOCompression:
|
|
squash.d = decompress.Lzo{}
|
|
case XZCompression:
|
|
squash.d = decompress.Xz{}
|
|
case LZ4Compression:
|
|
squash.d = decompress.Lz4{}
|
|
case ZSTDCompression:
|
|
squash.d = &decompress.Zstd{}
|
|
default:
|
|
return nil, errors.New("uh, I need to do this, OR something if very wrong")
|
|
}
|
|
if !squash.s.noFragments() && squash.s.FragCount > 0 {
|
|
fragOffsets := make([]uint64, int(math.Ceil(float64(squash.s.FragCount)/512)))
|
|
err = binary.Read(toreader.NewReader(r, int64(squash.s.FragTableStart)), binary.LittleEndian, &fragOffsets)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
squash.fragEntries = make([]fragEntry, squash.s.FragCount)
|
|
if len(fragOffsets) == 1 {
|
|
rdr := metadata.NewReader(toreader.NewReader(r, int64(fragOffsets[0])), squash.d)
|
|
err = binary.Read(rdr, binary.LittleEndian, &squash.fragEntries)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
toRead := squash.s.FragCount
|
|
var curRead uint32
|
|
var tmp []fragEntry
|
|
var rdr *metadata.Reader
|
|
var offset int
|
|
for i := range fragOffsets {
|
|
curRead = uint32(math.Min(512, float64(toRead)))
|
|
tmp = make([]fragEntry, curRead)
|
|
rdr = metadata.NewReader(toreader.NewReader(r, int64(fragOffsets[i])), squash.d)
|
|
err = binary.Read(rdr, binary.LittleEndian, &tmp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
offset = int(squash.s.FragCount - toRead)
|
|
for i := range tmp {
|
|
squash.fragEntries[offset+i] = tmp[i]
|
|
}
|
|
toRead -= curRead
|
|
}
|
|
}
|
|
}
|
|
if squash.s.IdCount > 0 {
|
|
idOffsets := make([]uint64, int(math.Ceil(float64(squash.s.IdCount)/2048)))
|
|
err = binary.Read(toreader.NewReader(r, int64(squash.s.IdTableStart)), binary.LittleEndian, &idOffsets)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
squash.ids = make([]uint32, squash.s.IdCount)
|
|
if len(idOffsets) == 1 {
|
|
rdr := metadata.NewReader(toreader.NewReader(r, int64(idOffsets[0])), squash.d)
|
|
err = binary.Read(rdr, binary.LittleEndian, &squash.ids)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
toRead := squash.s.IdCount
|
|
var curRead uint16
|
|
var tmp []uint32
|
|
var rdr *metadata.Reader
|
|
var offset int
|
|
for i := range idOffsets {
|
|
curRead = uint16(math.Min(2048, float64(toRead)))
|
|
tmp = make([]uint32, curRead)
|
|
rdr = metadata.NewReader(toreader.NewReader(r, int64(idOffsets[i])), squash.d)
|
|
err = binary.Read(rdr, binary.LittleEndian, &tmp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
offset = int(squash.s.IdCount - toRead)
|
|
for i := range tmp {
|
|
squash.ids[offset+i] = tmp[i]
|
|
}
|
|
toRead -= curRead
|
|
}
|
|
}
|
|
}
|
|
root, err := squash.inodeFromRef(squash.s.RootInodeRef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rootEnts, err := squash.readDirectory(root)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
enType := root.Type
|
|
if enType == inode.EDir {
|
|
enType = inode.Dir
|
|
}
|
|
squash.FS = &FS{
|
|
e: rootEnts,
|
|
File: &File{
|
|
rdr: &squash,
|
|
i: root,
|
|
e: directory.Entry{
|
|
Name: "",
|
|
Type: enType,
|
|
},
|
|
r: &squash,
|
|
},
|
|
}
|
|
return &squash, nil
|
|
}
|
|
|
|
// func (r *Reader) initExport() (err error) {
|
|
// num := int(math.Ceil(float64(r.s.InodeCount) / 1024))
|
|
// offsets := make([]uint64, num)
|
|
// err = binary.Read(toreader.NewReader(r.r, int64(r.s.ExportTableStart)), binary.LittleEndian, &offsets)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// left := r.s.InodeCount
|
|
// var toRead uint32
|
|
// var new []uint64
|
|
// var rdr *metadata.Reader
|
|
// for i := range offsets {
|
|
// rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offsets[i])), r.d)
|
|
// toRead = uint32(math.Min(1024, float64(left)))
|
|
// new = make([]uint64, toRead)
|
|
// err = binary.Read(rdr, binary.LittleEndian, &new)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// left -= toRead
|
|
// r.exportTable = append(r.exportTable, new...)
|
|
// }
|
|
// return nil
|
|
// }
|
|
|
|
// func (r *Reader) inode(index uint32) (i inode.Inode, err error) {
|
|
// if r.s.exportable() {
|
|
// if r.exportTable == nil {
|
|
// err = r.initExport()
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// }
|
|
// return r.inodeFromRef(r.exportTable[index-1])
|
|
// }
|
|
// err = errors.New("archive is not exportable")
|
|
// return
|
|
// }
|
|
|
|
// Returns the last time the archive was modified.
|
|
func (r Reader) ModTime() time.Time {
|
|
return time.Unix(int64(r.s.ModTime), 0)
|
|
}
|