Finished?
Everything seems to extract fine (though more testing is needed)
This commit is contained in:
@@ -60,3 +60,8 @@ func (r *Reader) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
return curRead, nil
|
return curRead, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Reader) Close() error {
|
||||||
|
r.dat = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
+53
-14
@@ -4,7 +4,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/CalebQ42/squashfs/internal/decompress"
|
|
||||||
"github.com/CalebQ42/squashfs/internal/metadata"
|
"github.com/CalebQ42/squashfs/internal/metadata"
|
||||||
"github.com/CalebQ42/squashfs/internal/toreader"
|
"github.com/CalebQ42/squashfs/internal/toreader"
|
||||||
"github.com/CalebQ42/squashfs/squashfs/data"
|
"github.com/CalebQ42/squashfs/squashfs/data"
|
||||||
@@ -17,26 +16,35 @@ type Base struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) baseFromInode(i *inode.Inode, name string) *Base {
|
func (r *Reader) BaseFromInode(i *inode.Inode, name string) *Base {
|
||||||
return &Base{Inode: i, Name: name}
|
return &Base{Inode: i, Name: name}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) baseFromEntry(e directory.Entry) (*Base, error) {
|
func (r *Reader) BaseFromEntry(e directory.Entry) (*Base, error) {
|
||||||
rdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.sup.InodeTableStart)+int64(e.BlockStart)), r.d)
|
rdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.Superblock.InodeTableStart)+int64(e.BlockStart)), r.d)
|
||||||
|
defer rdr.Close()
|
||||||
rdr.Read(make([]byte, e.Offset))
|
rdr.Read(make([]byte, e.Offset))
|
||||||
in, err := inode.Read(rdr, r.sup.BlockSize)
|
in, err := inode.Read(rdr, r.Superblock.BlockSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Base{Inode: in, Name: e.Name}, nil
|
return &Base{Inode: in, Name: e.Name}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Reader) BaseFromRef(ref uint64, name string) (*Base, error) {
|
||||||
|
in, err := r.inodeFromRef(ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Base{Inode: in, Name: name}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Base) Uid(r *Reader) (uint32, error) {
|
func (b *Base) Uid(r *Reader) (uint32, error) {
|
||||||
return r.id(b.Inode.UidInd)
|
return r.Id(b.Inode.UidInd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Base) Gid(r *Reader) (uint32, error) {
|
func (b *Base) Gid(r *Reader) (uint32, error) {
|
||||||
return r.id(b.Inode.GidInd)
|
return r.Id(b.Inode.GidInd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Base) IsDir() bool {
|
func (b *Base) IsDir() bool {
|
||||||
@@ -44,7 +52,35 @@ func (b *Base) IsDir() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Base) ToDir(r *Reader) (*Directory, error) {
|
func (b *Base) ToDir(r *Reader) (*Directory, error) {
|
||||||
return r.directoryFromInode(b.Inode, b.Name)
|
var blockStart uint32
|
||||||
|
var size uint32
|
||||||
|
var offset uint16
|
||||||
|
switch b.Inode.Type {
|
||||||
|
case inode.Dir:
|
||||||
|
blockStart = b.Inode.Data.(inode.Directory).BlockStart
|
||||||
|
size = uint32(b.Inode.Data.(inode.Directory).Size)
|
||||||
|
offset = b.Inode.Data.(inode.Directory).Offset
|
||||||
|
case inode.EDir:
|
||||||
|
blockStart = b.Inode.Data.(inode.EDirectory).BlockStart
|
||||||
|
size = b.Inode.Data.(inode.EDirectory).Size
|
||||||
|
offset = b.Inode.Data.(inode.EDirectory).Offset
|
||||||
|
default:
|
||||||
|
return nil, errors.New("not a directory")
|
||||||
|
}
|
||||||
|
dirRdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.Superblock.DirTableStart)+int64(blockStart)), r.d)
|
||||||
|
defer dirRdr.Close()
|
||||||
|
_, err := dirRdr.Read(make([]byte, offset))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
entries, err := directory.ReadDirectory(dirRdr, size)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Directory{
|
||||||
|
Base: *b,
|
||||||
|
Entries: entries,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Base) IsRegular() bool {
|
func (b *Base) IsRegular() bool {
|
||||||
@@ -58,36 +94,39 @@ func (b *Base) GetRegFileReaders(r *Reader) (*data.Reader, *data.FullReader, err
|
|||||||
var blockStart uint64
|
var blockStart uint64
|
||||||
var fragIndex uint32
|
var fragIndex uint32
|
||||||
var fragOffset uint32
|
var fragOffset uint32
|
||||||
|
var fragSize uint64
|
||||||
var sizes []uint32
|
var sizes []uint32
|
||||||
if b.Inode.Type == inode.Fil {
|
if b.Inode.Type == inode.Fil {
|
||||||
blockStart = uint64(b.Inode.Data.(inode.File).BlockStart)
|
blockStart = uint64(b.Inode.Data.(inode.File).BlockStart)
|
||||||
fragIndex = b.Inode.Data.(inode.File).FragInd
|
fragIndex = b.Inode.Data.(inode.File).FragInd
|
||||||
fragOffset = b.Inode.Data.(inode.File).FragOffset
|
fragOffset = b.Inode.Data.(inode.File).FragOffset
|
||||||
sizes = b.Inode.Data.(inode.File).BlockSizes
|
sizes = b.Inode.Data.(inode.File).BlockSizes
|
||||||
|
fragSize = uint64(b.Inode.Data.(inode.File).Size % r.Superblock.BlockSize)
|
||||||
} else {
|
} else {
|
||||||
blockStart = b.Inode.Data.(inode.EFile).BlockStart
|
blockStart = b.Inode.Data.(inode.EFile).BlockStart
|
||||||
fragIndex = b.Inode.Data.(inode.EFile).FragInd
|
fragIndex = b.Inode.Data.(inode.EFile).FragInd
|
||||||
fragOffset = b.Inode.Data.(inode.EFile).FragOffset
|
fragOffset = b.Inode.Data.(inode.EFile).FragOffset
|
||||||
sizes = b.Inode.Data.(inode.EFile).BlockSizes
|
sizes = b.Inode.Data.(inode.EFile).BlockSizes
|
||||||
|
fragSize = b.Inode.Data.(inode.EFile).Size % uint64(r.Superblock.BlockSize)
|
||||||
}
|
}
|
||||||
frag := func(rdr io.ReaderAt, d decompress.Decompressor) (*data.Reader, error) {
|
frag := func() (io.Reader, error) {
|
||||||
ent, err := r.fragEntry(fragIndex)
|
ent, err := r.fragEntry(fragIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
frag := data.NewReader(toreader.NewReader(r.r, int64(ent.start)), r.d, []uint32{ent.size})
|
frag := data.NewReader(toreader.NewReader(r.r, int64(ent.Start)), r.d, []uint32{ent.Size}, uint64(r.Superblock.BlockSize), r.Superblock.BlockSize)
|
||||||
frag.Read(make([]byte, fragOffset))
|
frag.Read(make([]byte, fragOffset))
|
||||||
return frag, nil
|
return io.LimitReader(frag, int64(fragSize)), nil
|
||||||
}
|
}
|
||||||
outRdr := data.NewReader(toreader.NewReader(r.r, int64(blockStart)), r.d, sizes)
|
outRdr := data.NewReader(toreader.NewReader(r.r, int64(blockStart)), r.d, sizes, fragSize, r.Superblock.BlockSize)
|
||||||
if fragIndex != 0xffffffff {
|
if fragIndex != 0xffffffff {
|
||||||
f, err := frag(r.r, r.d)
|
f, err := frag()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
outRdr.AddFrag(f)
|
outRdr.AddFrag(f)
|
||||||
}
|
}
|
||||||
outFull := data.NewFullReader(r.r, int64(blockStart), r.d, sizes)
|
outFull := data.NewFullReader(r.r, int64(blockStart), r.d, sizes, fragSize, r.Superblock.BlockSize)
|
||||||
if fragIndex != 0xffffffff {
|
if fragIndex != 0xffffffff {
|
||||||
outFull.AddFrag(frag)
|
outFull.AddFrag(frag)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/CalebQ42/squashfs/internal/decompress"
|
"github.com/CalebQ42/squashfs/internal/decompress"
|
||||||
"github.com/CalebQ42/squashfs/internal/toreader"
|
"github.com/CalebQ42/squashfs/internal/toreader"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FragReaderConstructor func(io.ReaderAt, decompress.Decompressor) (*Reader, error)
|
type FragReaderConstructor func() (io.Reader, error)
|
||||||
|
|
||||||
type FullReader struct {
|
type FullReader struct {
|
||||||
r io.ReaderAt
|
r io.ReaderAt
|
||||||
@@ -19,16 +20,20 @@ type FullReader struct {
|
|||||||
retPool *sync.Pool
|
retPool *sync.Pool
|
||||||
sizes []uint32
|
sizes []uint32
|
||||||
initialOffset int64
|
initialOffset int64
|
||||||
|
finalBlockSize uint64
|
||||||
|
blockSize uint32
|
||||||
goroutineLimit uint16
|
goroutineLimit uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFullReader(r io.ReaderAt, initialOffset int64, d decompress.Decompressor, sizes []uint32) *FullReader {
|
func NewFullReader(r io.ReaderAt, initialOffset int64, d decompress.Decompressor, sizes []uint32, finalBlockSize uint64, blockSize uint32) *FullReader {
|
||||||
return &FullReader{
|
return &FullReader{
|
||||||
r: r,
|
r: r,
|
||||||
d: d,
|
d: d,
|
||||||
sizes: sizes,
|
sizes: sizes,
|
||||||
initialOffset: initialOffset,
|
initialOffset: initialOffset,
|
||||||
goroutineLimit: 10,
|
goroutineLimit: 10,
|
||||||
|
finalBlockSize: finalBlockSize,
|
||||||
|
blockSize: blockSize,
|
||||||
retPool: &sync.Pool{
|
retPool: &sync.Pool{
|
||||||
New: func() any {
|
New: func() any {
|
||||||
return &retValue{}
|
return &retValue{}
|
||||||
@@ -55,6 +60,16 @@ func (r *FullReader) process(index uint64, fileOffset uint64, retChan chan *retV
|
|||||||
ret := r.retPool.Get().(*retValue)
|
ret := r.retPool.Get().(*retValue)
|
||||||
ret.index = index
|
ret.index = index
|
||||||
realSize := r.sizes[index] &^ (1 << 24)
|
realSize := r.sizes[index] &^ (1 << 24)
|
||||||
|
if realSize == 0 {
|
||||||
|
if index == uint64(len(r.sizes))-1 && r.frag == nil {
|
||||||
|
ret.data = make([]byte, r.finalBlockSize)
|
||||||
|
} else {
|
||||||
|
ret.data = make([]byte, r.blockSize)
|
||||||
|
}
|
||||||
|
ret.err = nil
|
||||||
|
retChan <- ret
|
||||||
|
return
|
||||||
|
}
|
||||||
ret.data = make([]byte, realSize)
|
ret.data = make([]byte, realSize)
|
||||||
ret.err = binary.Read(toreader.NewReader(r.r, int64(r.initialOffset)+int64(fileOffset)), binary.LittleEndian, &ret.data)
|
ret.err = binary.Read(toreader.NewReader(r.r, int64(r.initialOffset)+int64(fileOffset)), binary.LittleEndian, &ret.data)
|
||||||
if r.sizes[index] == realSize {
|
if r.sizes[index] == realSize {
|
||||||
@@ -71,7 +86,7 @@ func (r *FullReader) WriteTo(w io.Writer) (int64, error) {
|
|||||||
cache := make(map[uint64]*retValue)
|
cache := make(map[uint64]*retValue)
|
||||||
var errCache []error
|
var errCache []error
|
||||||
retChan := make(chan *retValue, r.goroutineLimit)
|
retChan := make(chan *retValue, r.goroutineLimit)
|
||||||
for i := uint64(0); i < uint64(len(r.sizes))/uint64(r.goroutineLimit); i++ {
|
for i := uint64(0); i < uint64(math.Ceil(float64(len(r.sizes))/float64(r.goroutineLimit))); i++ {
|
||||||
toProcess = uint16(len(r.sizes)) - (uint16(i) * r.goroutineLimit)
|
toProcess = uint16(len(r.sizes)) - (uint16(i) * r.goroutineLimit)
|
||||||
if toProcess > r.goroutineLimit {
|
if toProcess > r.goroutineLimit {
|
||||||
toProcess = r.goroutineLimit
|
toProcess = r.goroutineLimit
|
||||||
@@ -139,12 +154,17 @@ func (r *FullReader) WriteTo(w io.Writer) (int64, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if r.frag != nil {
|
if r.frag != nil {
|
||||||
rdr, err := r.frag(r.r, r.d)
|
rdr, err := r.frag()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wrote, err
|
return wrote, err
|
||||||
}
|
}
|
||||||
wr, err := io.Copy(w, rdr)
|
wr, err := io.Copy(w, rdr)
|
||||||
wrote += wr
|
wrote += wr
|
||||||
|
if l, ok := rdr.(*io.LimitedReader); ok {
|
||||||
|
if cl, ok := l.R.(io.Closer); ok {
|
||||||
|
cl.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wrote, err
|
return wrote, err
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-21
@@ -8,24 +8,28 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
r io.Reader
|
r io.Reader
|
||||||
d decompress.Decompressor
|
d decompress.Decompressor
|
||||||
frag *Reader
|
frag io.Reader
|
||||||
sizes []uint32
|
sizes []uint32
|
||||||
dat []byte
|
dat []byte
|
||||||
curOffset uint16
|
curOffset int
|
||||||
curIndex uint64
|
curIndex uint64
|
||||||
|
finalBlockSize uint64
|
||||||
|
blockSize uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReader(r io.Reader, d decompress.Decompressor, sizes []uint32) *Reader {
|
func NewReader(r io.Reader, d decompress.Decompressor, sizes []uint32, finalBlockSize uint64, blockSize uint32) *Reader {
|
||||||
return &Reader{
|
return &Reader{
|
||||||
r: r,
|
r: r,
|
||||||
d: d,
|
d: d,
|
||||||
sizes: sizes,
|
sizes: sizes,
|
||||||
|
finalBlockSize: finalBlockSize,
|
||||||
|
blockSize: blockSize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) AddFrag(fragRdr *Reader) {
|
func (r *Reader) AddFrag(fragRdr io.Reader) {
|
||||||
r.frag = fragRdr
|
r.frag = fragRdr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,13 +37,21 @@ func (r *Reader) advance() error {
|
|||||||
r.curOffset = 0
|
r.curOffset = 0
|
||||||
defer func() { r.curIndex++ }()
|
defer func() { r.curIndex++ }()
|
||||||
var err error
|
var err error
|
||||||
if r.curIndex == uint64(len(r.sizes))-1 && r.frag != nil {
|
if r.curIndex == uint64(len(r.sizes)) && r.frag != nil {
|
||||||
r.dat, err = io.ReadAll(r.frag)
|
r.dat, err = io.ReadAll(r.frag)
|
||||||
return err
|
return err
|
||||||
} else if r.curIndex >= uint64(len(r.sizes))-1 {
|
} else if r.curIndex >= uint64(len(r.sizes)) {
|
||||||
return io.EOF
|
return io.EOF
|
||||||
}
|
}
|
||||||
realSize := r.sizes[r.curIndex] &^ 0x8000
|
realSize := r.sizes[r.curIndex] &^ (1 << 24)
|
||||||
|
if realSize == 0 {
|
||||||
|
if r.curIndex == uint64(len(r.sizes))-1 && r.frag == nil {
|
||||||
|
r.dat = make([]byte, r.finalBlockSize)
|
||||||
|
} else {
|
||||||
|
r.dat = make([]byte, r.blockSize)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
r.dat = make([]byte, realSize)
|
r.dat = make([]byte, realSize)
|
||||||
err = binary.Read(r.r, binary.LittleEndian, &r.dat)
|
err = binary.Read(r.r, binary.LittleEndian, &r.dat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -56,17 +68,17 @@ func (r *Reader) Read(b []byte) (int, error) {
|
|||||||
curRead := 0
|
curRead := 0
|
||||||
var toRead int
|
var toRead int
|
||||||
for curRead < len(b) {
|
for curRead < len(b) {
|
||||||
if r.curOffset >= uint16(len(r.dat)) {
|
if r.curOffset >= len(r.dat) {
|
||||||
if err := r.advance(); err != nil {
|
if err := r.advance(); err != nil {
|
||||||
return curRead, err
|
return curRead, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toRead = len(b) - curRead
|
toRead = len(b) - curRead
|
||||||
if toRead > len(r.dat)-int(r.curOffset) {
|
if toRead > len(r.dat)-r.curOffset {
|
||||||
toRead = len(r.dat) - int(r.curOffset)
|
toRead = len(r.dat) - r.curOffset
|
||||||
}
|
}
|
||||||
copy(b[curRead:], r.dat[r.curOffset:int(r.curOffset)+toRead])
|
toRead = copy(b[curRead:], r.dat[r.curOffset:r.curOffset+toRead])
|
||||||
r.curOffset += uint16(toRead)
|
r.curOffset += toRead
|
||||||
curRead += toRead
|
curRead += toRead
|
||||||
}
|
}
|
||||||
return curRead, nil
|
return curRead, nil
|
||||||
@@ -74,7 +86,11 @@ func (r *Reader) Read(b []byte) (int, error) {
|
|||||||
|
|
||||||
func (r *Reader) Close() error {
|
func (r *Reader) Close() error {
|
||||||
if r.frag != nil {
|
if r.frag != nil {
|
||||||
return r.frag.Close()
|
if l, ok := r.frag.(*io.LimitedReader); ok {
|
||||||
|
if cl, ok := l.R.(io.Closer); ok {
|
||||||
|
cl.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
r.dat = nil
|
r.dat = nil
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+12
-13
@@ -2,6 +2,7 @@ package squashfs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
@@ -18,7 +19,12 @@ type Directory struct {
|
|||||||
Entries []directory.Entry
|
Entries []directory.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) directoryFromInode(i *inode.Inode, name string) (*Directory, error) {
|
func (r *Reader) directoryFromRef(ref uint64, name string) (*Directory, error) {
|
||||||
|
i, err := r.inodeFromRef(ref)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("yo")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
var blockStart uint32
|
var blockStart uint32
|
||||||
var size uint32
|
var size uint32
|
||||||
var offset uint16
|
var offset uint16
|
||||||
@@ -34,8 +40,9 @@ func (r *Reader) directoryFromInode(i *inode.Inode, name string) (*Directory, er
|
|||||||
default:
|
default:
|
||||||
return nil, errors.New("not a directory")
|
return nil, errors.New("not a directory")
|
||||||
}
|
}
|
||||||
dirRdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.sup.DirTableStart)+int64(blockStart)), r.d)
|
dirRdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.Superblock.DirTableStart)+int64(blockStart)), r.d)
|
||||||
_, err := dirRdr.Read(make([]byte, offset))
|
defer dirRdr.Close()
|
||||||
|
_, err = dirRdr.Read(make([]byte, offset))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -44,19 +51,11 @@ func (r *Reader) directoryFromInode(i *inode.Inode, name string) (*Directory, er
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Directory{
|
return &Directory{
|
||||||
Base: *r.baseFromInode(i, name),
|
Base: *r.BaseFromInode(i, name),
|
||||||
Entries: entries,
|
Entries: entries,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) directoryFromRef(ref uint64, name string) (*Directory, error) {
|
|
||||||
in, err := r.inodeFromRef(ref)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return r.directoryFromInode(in, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Directory) Open(r *Reader, path string) (*Base, error) {
|
func (d *Directory) Open(r *Reader, path string) (*Base, error) {
|
||||||
path = filepath.Clean(path)
|
path = filepath.Clean(path)
|
||||||
if path == "." || path == "" {
|
if path == "." || path == "" {
|
||||||
@@ -69,7 +68,7 @@ func (d *Directory) Open(r *Reader, path string) (*Base, error) {
|
|||||||
if !found {
|
if !found {
|
||||||
return nil, fs.ErrNotExist
|
return nil, fs.ErrNotExist
|
||||||
}
|
}
|
||||||
b, err := r.baseFromEntry(d.Entries[i])
|
b, err := r.BaseFromEntry(d.Entries[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package squashfs
|
package squashfs
|
||||||
|
|
||||||
type fragEntry struct {
|
type fragEntry struct {
|
||||||
start uint64
|
Start uint64
|
||||||
size uint32
|
Size uint32
|
||||||
_ uint32
|
_ uint32
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -7,11 +7,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (r *Reader) inodeFromRef(ref uint64) (*inode.Inode, error) {
|
func (r *Reader) inodeFromRef(ref uint64) (*inode.Inode, error) {
|
||||||
offset, meta := (ref>>16)+r.sup.InodeTableStart, ref&0xFFFF
|
offset, meta := (ref>>16)+r.Superblock.InodeTableStart, ref&0xFFFF
|
||||||
rdr := metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
|
rdr := metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
|
||||||
|
defer rdr.Close()
|
||||||
_, err := rdr.Read(make([]byte, meta))
|
_, err := rdr.Read(make([]byte, meta))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return inode.Read(rdr, r.sup.BlockSize)
|
return inode.Read(rdr, r.Superblock.BlockSize)
|
||||||
}
|
}
|
||||||
|
|||||||
+68
-34
@@ -8,7 +8,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/CalebQ42/squashfs/internal/decompress"
|
"github.com/CalebQ42/squashfs/internal/decompress"
|
||||||
|
"github.com/CalebQ42/squashfs/internal/metadata"
|
||||||
"github.com/CalebQ42/squashfs/internal/toreader"
|
"github.com/CalebQ42/squashfs/internal/toreader"
|
||||||
|
"github.com/CalebQ42/squashfs/squashfs/inode"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The types of compression supported by squashfs
|
// The types of compression supported by squashfs
|
||||||
@@ -31,30 +33,30 @@ var (
|
|||||||
type Reader struct {
|
type Reader struct {
|
||||||
r io.ReaderAt
|
r io.ReaderAt
|
||||||
d decompress.Decompressor
|
d decompress.Decompressor
|
||||||
root *Directory
|
Root *Directory
|
||||||
fragTable []fragEntry
|
fragTable []fragEntry
|
||||||
idTable []uint32
|
idTable []uint32
|
||||||
exportTable []uint64
|
exportTable []uint64
|
||||||
sup superblock
|
Superblock superblock
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReader(r io.ReaderAt) (rdr *Reader, err error) {
|
func NewReader(r io.ReaderAt) (rdr *Reader, err error) {
|
||||||
rdr = new(Reader)
|
rdr = new(Reader)
|
||||||
rdr.r = r
|
rdr.r = r
|
||||||
err = binary.Read(toreader.NewReader(r, 0), binary.LittleEndian, &rdr.sup)
|
err = binary.Read(toreader.NewReader(r, 0), binary.LittleEndian, &rdr.Superblock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Join(errors.New("failed to read superblock"), err)
|
return nil, errors.Join(errors.New("failed to read superblock"), err)
|
||||||
}
|
}
|
||||||
if !rdr.sup.checkMagic() {
|
if !rdr.Superblock.ValidMagic() {
|
||||||
return nil, ErrorMagic
|
return nil, ErrorMagic
|
||||||
}
|
}
|
||||||
if !rdr.sup.checkBlockLog() {
|
if !rdr.Superblock.ValidBlockLog() {
|
||||||
return nil, ErrorLog
|
return nil, ErrorLog
|
||||||
}
|
}
|
||||||
if !rdr.sup.checkVersion() {
|
if !rdr.Superblock.ValidVersion() {
|
||||||
return nil, ErrorVersion
|
return nil, ErrorVersion
|
||||||
}
|
}
|
||||||
switch rdr.sup.CompType {
|
switch rdr.Superblock.CompType {
|
||||||
case ZlibCompression:
|
case ZlibCompression:
|
||||||
rdr.d = decompress.Zlib{}
|
rdr.d = decompress.Zlib{}
|
||||||
case LZMACompression:
|
case LZMACompression:
|
||||||
@@ -70,7 +72,7 @@ func NewReader(r io.ReaderAt) (rdr *Reader, err error) {
|
|||||||
default:
|
default:
|
||||||
return nil, errors.New("invalid compression type. possible corrupted archive")
|
return nil, errors.New("invalid compression type. possible corrupted archive")
|
||||||
}
|
}
|
||||||
rdr.root, err = rdr.directoryFromRef(rdr.sup.RootInodeRef, "")
|
rdr.Root, err = rdr.directoryFromRef(rdr.Superblock.RootInodeRef, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Join(errors.New("failed to read root directory"), err)
|
return nil, errors.Join(errors.New("failed to read root directory"), err)
|
||||||
}
|
}
|
||||||
@@ -79,36 +81,44 @@ func NewReader(r io.ReaderAt) (rdr *Reader, err error) {
|
|||||||
|
|
||||||
// Returns the last time the archive was modified.
|
// Returns the last time the archive was modified.
|
||||||
func (r *Reader) ModTime() time.Time {
|
func (r *Reader) ModTime() time.Time {
|
||||||
return time.Unix(int64(r.sup.ModTime), 0)
|
return time.Unix(int64(r.Superblock.ModTime), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a uid/gid at the given index. Lazily populates the reader's id table as necessary.
|
// Get a uid/gid at the given index. Lazily populates the reader's Id table as necessary.
|
||||||
func (r *Reader) id(i uint16) (uint32, error) {
|
func (r *Reader) Id(i uint16) (uint32, error) {
|
||||||
if len(r.idTable) > int(i) {
|
if len(r.idTable) > int(i) {
|
||||||
return r.idTable[i], nil
|
return r.idTable[i], nil
|
||||||
} else if i >= r.sup.IdCount {
|
} else if i >= r.Superblock.IdCount {
|
||||||
return 0, errors.New("id out of bounds")
|
return 0, errors.New("id out of bounds")
|
||||||
}
|
}
|
||||||
// Populate the id table as needed
|
// Populate the id table as needed
|
||||||
blockNum := uint16(math.Ceil(float64(i) / 2048))
|
var blockNum uint32
|
||||||
|
if i != 0 { // If i == 0, we go negatives causing issues with uint32s
|
||||||
|
blockNum = uint32(math.Ceil(float64(i)/2048)) - 1
|
||||||
|
} else {
|
||||||
|
blockNum = 0
|
||||||
|
}
|
||||||
blocksRead := len(r.idTable) / 2048
|
blocksRead := len(r.idTable) / 2048
|
||||||
blocksToRead := int(blockNum) - blocksRead
|
blocksToRead := int(blockNum) - blocksRead + 1
|
||||||
|
|
||||||
var offset uint64
|
var offset uint64
|
||||||
var idsToRead uint16
|
var idsToRead uint16
|
||||||
var idsTmp []uint32
|
var idsTmp []uint32
|
||||||
var err error
|
var err error
|
||||||
for i := blocksRead; i < int(blockNum)+blocksToRead; i++ {
|
var rdr *metadata.Reader
|
||||||
err = binary.Read(toreader.NewReader(r.r, int64(r.sup.IdTableStart)+int64(8*i)), binary.LittleEndian, &offset)
|
for i := blocksRead; i < int(blocksRead)+blocksToRead; i++ {
|
||||||
|
err = binary.Read(toreader.NewReader(r.r, int64(r.Superblock.IdTableStart)+int64(8*i)), binary.LittleEndian, &offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
idsToRead = r.sup.IdCount - uint16(len(r.idTable))
|
idsToRead = r.Superblock.IdCount - uint16(len(r.idTable))
|
||||||
if idsToRead > 2048 {
|
if idsToRead > 2048 {
|
||||||
idsToRead = 2048
|
idsToRead = 2048
|
||||||
}
|
}
|
||||||
idsTmp = make([]uint32, idsToRead)
|
idsTmp = make([]uint32, idsToRead)
|
||||||
err = binary.Read(toreader.NewReader(r.r, int64(offset)), binary.LittleEndian, &idsTmp)
|
rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
|
||||||
|
err = binary.Read(rdr, binary.LittleEndian, &idsTmp)
|
||||||
|
rdr.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -121,29 +131,37 @@ func (r *Reader) id(i uint16) (uint32, error) {
|
|||||||
func (r *Reader) fragEntry(i uint32) (fragEntry, error) {
|
func (r *Reader) fragEntry(i uint32) (fragEntry, error) {
|
||||||
if len(r.fragTable) > int(i) {
|
if len(r.fragTable) > int(i) {
|
||||||
return r.fragTable[i], nil
|
return r.fragTable[i], nil
|
||||||
} else if i >= r.sup.FragCount {
|
} else if i >= r.Superblock.FragCount {
|
||||||
return fragEntry{}, errors.New("fragment out of bounds")
|
return fragEntry{}, errors.New("fragment out of bounds")
|
||||||
}
|
}
|
||||||
// Populate the fragment table as needed
|
// Populate the fragment table as needed
|
||||||
blockNum := uint32(math.Ceil(float64(i) / 512))
|
var blockNum uint32
|
||||||
|
if i != 0 { // If i == 0, we go negatives causing issues with uint32s
|
||||||
|
blockNum = uint32(math.Ceil(float64(i)/512)) - 1
|
||||||
|
} else {
|
||||||
|
blockNum = 0
|
||||||
|
}
|
||||||
blocksRead := len(r.fragTable) / 512
|
blocksRead := len(r.fragTable) / 512
|
||||||
blocksToRead := int(blockNum) - blocksRead
|
blocksToRead := int(blockNum) - blocksRead + 1
|
||||||
|
|
||||||
var offset uint64
|
var offset uint64
|
||||||
var fragsToRead uint32
|
var fragsToRead uint32
|
||||||
var fragsTmp []fragEntry
|
var fragsTmp []fragEntry
|
||||||
var err error
|
var err error
|
||||||
for i := blocksRead; i < int(blockNum)+blocksToRead; i++ {
|
var rdr *metadata.Reader
|
||||||
err = binary.Read(toreader.NewReader(r.r, int64(r.sup.FragTableStart)+int64(8*i)), binary.LittleEndian, &offset)
|
for i := blocksRead; i < int(blocksRead)+blocksToRead; i++ {
|
||||||
|
err = binary.Read(toreader.NewReader(r.r, int64(r.Superblock.FragTableStart)+int64(8*i)), binary.LittleEndian, &offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fragEntry{}, err
|
return fragEntry{}, err
|
||||||
}
|
}
|
||||||
fragsToRead = r.sup.FragCount - uint32(len(r.fragTable))
|
fragsToRead = r.Superblock.FragCount - uint32(len(r.fragTable))
|
||||||
if fragsToRead > 512 {
|
if fragsToRead > 512 {
|
||||||
fragsToRead = 512
|
fragsToRead = 512
|
||||||
}
|
}
|
||||||
fragsTmp = make([]fragEntry, fragsToRead)
|
fragsTmp = make([]fragEntry, fragsToRead)
|
||||||
err = binary.Read(toreader.NewReader(r.r, int64(offset)), binary.LittleEndian, &fragsTmp)
|
rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
|
||||||
|
err = binary.Read(rdr, binary.LittleEndian, &fragsTmp)
|
||||||
|
rdr.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fragEntry{}, err
|
return fragEntry{}, err
|
||||||
}
|
}
|
||||||
@@ -154,34 +172,42 @@ func (r *Reader) fragEntry(i uint32) (fragEntry, error) {
|
|||||||
|
|
||||||
// Get an inode reference at the given index. Lazily populates the reader's export table as necessary.
|
// Get an inode reference at the given index. Lazily populates the reader's export table as necessary.
|
||||||
func (r *Reader) inodeRef(i uint32) (uint64, error) {
|
func (r *Reader) inodeRef(i uint32) (uint64, error) {
|
||||||
if !r.sup.exportable() {
|
if !r.Superblock.Exportable() {
|
||||||
return 0, ErrorNotExportable
|
return 0, ErrorNotExportable
|
||||||
}
|
}
|
||||||
if len(r.exportTable) > int(i) {
|
if len(r.exportTable) > int(i) {
|
||||||
return r.exportTable[i], nil
|
return r.exportTable[i], nil
|
||||||
} else if i >= r.sup.InodeCount {
|
} else if i >= r.Superblock.InodeCount {
|
||||||
return 0, errors.New("inode out of bounds")
|
return 0, errors.New("inode out of bounds")
|
||||||
}
|
}
|
||||||
// Populate the export table as neede
|
// Populate the export table as needed
|
||||||
blockNum := uint32(math.Ceil(float64(i) / 1024))
|
var blockNum uint32
|
||||||
|
if i != 0 { // If i == 0, we go negatives causing issues with uint32s
|
||||||
|
blockNum = uint32(math.Ceil(float64(i)/1024)) - 1
|
||||||
|
} else {
|
||||||
|
blockNum = 0
|
||||||
|
}
|
||||||
blocksRead := len(r.exportTable) / 1024
|
blocksRead := len(r.exportTable) / 1024
|
||||||
blocksToRead := int(blockNum) - blocksRead
|
blocksToRead := int(blockNum) - blocksRead + 1
|
||||||
|
|
||||||
var offset uint64
|
var offset uint64
|
||||||
var refsToRead uint32
|
var refsToRead uint32
|
||||||
var refsTmp []uint64
|
var refsTmp []uint64
|
||||||
var err error
|
var err error
|
||||||
for i := blocksRead; i < int(blockNum)+blocksToRead; i++ {
|
var rdr *metadata.Reader
|
||||||
err = binary.Read(toreader.NewReader(r.r, int64(r.sup.ExportTableStart)+int64(8*i)), binary.LittleEndian, &offset)
|
for i := blocksRead; i < int(blocksRead)+blocksToRead; i++ {
|
||||||
|
err = binary.Read(toreader.NewReader(r.r, int64(r.Superblock.ExportTableStart)+int64(8*i)), binary.LittleEndian, &offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
refsToRead = r.sup.InodeCount - uint32(len(r.exportTable))
|
refsToRead = r.Superblock.InodeCount - uint32(len(r.exportTable))
|
||||||
if refsToRead > 1024 {
|
if refsToRead > 1024 {
|
||||||
refsToRead = 1024
|
refsToRead = 1024
|
||||||
}
|
}
|
||||||
refsTmp = make([]uint64, refsToRead)
|
refsTmp = make([]uint64, refsToRead)
|
||||||
err = binary.Read(toreader.NewReader(r.r, int64(offset)), binary.LittleEndian, &refsTmp)
|
rdr = metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
|
||||||
|
err = binary.Read(rdr, binary.LittleEndian, &refsTmp)
|
||||||
|
rdr.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -189,3 +215,11 @@ func (r *Reader) inodeRef(i uint32) (uint64, error) {
|
|||||||
}
|
}
|
||||||
return r.exportTable[i], nil
|
return r.exportTable[i], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Reader) Inode(i uint32) (*inode.Inode, error) {
|
||||||
|
ref, err := r.inodeRef(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return r.inodeFromRef(ref)
|
||||||
|
}
|
||||||
|
|||||||
+59
-14
@@ -1,21 +1,18 @@
|
|||||||
package squashfs
|
package squashfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/CalebQ42/squashfs/squashfs/inode"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
squashfsURL = "https://darkstorm.tech/files/LinuxPATest.sfs"
|
squashfsURL = "https://darkstorm.tech/files/LinuxPATest.sfs"
|
||||||
squashfsName = "LinuxPATest.sfs"
|
squashfsName = "LinuxPATest.sfs"
|
||||||
|
|
||||||
// filePath = "PortableApps/Notepad++Portable/App/DefaultData/Config/contextMenu.xml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func preTest(dir string) (fil *os.File, err error) {
|
func preTest(dir string) (fil *os.File, err error) {
|
||||||
@@ -62,26 +59,74 @@ func TestReader(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = checkDir(rdr, rdr.root)
|
path := filepath.Join(tmpDir, "extractTest")
|
||||||
|
os.RemoveAll(path)
|
||||||
|
os.MkdirAll(path, 0777)
|
||||||
|
err = extractToDir(rdr, &rdr.Root.Base, path)
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDir(rdr *Reader, d *Directory) error {
|
var singleFile = "PortableApps/CPU-X/CPU-X-v4.2.0-x86_64.AppImage"
|
||||||
for _, e := range d.Entries {
|
|
||||||
if e.InodeType == inode.Dir {
|
func TestSingleFile(t *testing.T) {
|
||||||
b, err := d.Open(rdr, e.Name)
|
tmpDir := "../testing"
|
||||||
|
fil, err := preTest(tmpDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer fil.Close()
|
||||||
|
rdr, err := NewReader(fil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
path := filepath.Join(tmpDir, "extractTest")
|
||||||
|
os.RemoveAll(path)
|
||||||
|
os.MkdirAll(path, 0777)
|
||||||
|
b, err := rdr.Root.Open(rdr, singleFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = extractToDir(rdr, b, path)
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractToDir(rdr *Reader, b *Base, folder string) error {
|
||||||
|
path := filepath.Join(folder, b.Name)
|
||||||
|
if b.IsDir() {
|
||||||
|
d, err := b.ToDir(rdr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(path, 0777)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var nestBast *Base
|
||||||
|
for _, e := range d.Entries {
|
||||||
|
nestBast, err = rdr.BaseFromEntry(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
d, err := b.ToDir(rdr)
|
err = extractToDir(rdr, nestBast, path)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = checkDir(rdr, d)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if b.IsRegular() {
|
||||||
|
_, full, err := b.GetRegFileReaders(rdr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("yo", path)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fil, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = full.WriteTo(fil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Successfully extracted file:", b.Name)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-14
@@ -24,57 +24,57 @@ type superblock struct {
|
|||||||
ExportTableStart uint64
|
ExportTableStart uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) checkMagic() bool {
|
func (s superblock) ValidMagic() bool {
|
||||||
return s.Magic == 0x73717368
|
return s.Magic == 0x73717368
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) checkBlockLog() bool {
|
func (s superblock) ValidBlockLog() bool {
|
||||||
return s.BlockLog == uint16(math.Log2(float64(s.BlockSize)))
|
return s.BlockLog == uint16(math.Log2(float64(s.BlockSize)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) checkVersion() bool {
|
func (s superblock) ValidVersion() bool {
|
||||||
return s.VerMaj == 4 && s.VerMin == 0
|
return s.VerMaj == 4 && s.VerMin == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) uncompressedInodes() bool {
|
func (s superblock) UncompressedInodes() bool {
|
||||||
return s.Flags&0x1 == 0x1
|
return s.Flags&0x1 == 0x1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) uncompressedData() bool {
|
func (s superblock) UncompressedData() bool {
|
||||||
return s.Flags&0x2 == 0x2
|
return s.Flags&0x2 == 0x2
|
||||||
}
|
}
|
||||||
func (s superblock) uncompressedFragments() bool {
|
func (s superblock) UncompressedFragments() bool {
|
||||||
return s.Flags&0x8 == 0x8
|
return s.Flags&0x8 == 0x8
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) noFragments() bool {
|
func (s superblock) NoFragments() bool {
|
||||||
return s.Flags&0x10 == 0x10
|
return s.Flags&0x10 == 0x10
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) alwaysFragment() bool {
|
func (s superblock) AlwaysFragment() bool {
|
||||||
return s.Flags&0x20 == 0x20
|
return s.Flags&0x20 == 0x20
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) duplicates() bool {
|
func (s superblock) Duplicates() bool {
|
||||||
return s.Flags&0x40 == 0x40
|
return s.Flags&0x40 == 0x40
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) exportable() bool {
|
func (s superblock) Exportable() bool {
|
||||||
return s.Flags&0x80 == 0x80
|
return s.Flags&0x80 == 0x80
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) uncompressedXattrs() bool {
|
func (s superblock) UncompressedXattrs() bool {
|
||||||
return s.Flags&0x100 == 0x100
|
return s.Flags&0x100 == 0x100
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) noXattrs() bool {
|
func (s superblock) NoXattrs() bool {
|
||||||
return s.Flags&0x200 == 0x200
|
return s.Flags&0x200 == 0x200
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) compressionOptions() bool {
|
func (s superblock) CompressionOptions() bool {
|
||||||
return s.Flags&0x400 == 0x400
|
return s.Flags&0x400 == 0x400
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s superblock) uncompressedIDs() bool {
|
func (s superblock) UncompressedIDs() bool {
|
||||||
return s.Flags&0x800 == 0x800
|
return s.Flags&0x800 == 0x800
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user