Compare commits

...

11 Commits

Author SHA1 Message Date
Caleb Gardner 820e06e792 fuse Serve in goroutine 2022-12-17 17:06:28 -06:00
Caleb Gardner 4f8f5f6928 Tweaks to decode interface 2022-12-17 16:47:33 -06:00
Caleb Gardner 1b5078c7bd Messing around with optimizations. 2022-12-14 13:48:22 -06:00
Caleb Gardner 56fdba2f28 Merge pull request #17 from CalebQ42/fuseBraz
Fuse SUCCESS
2022-12-03 02:50:17 -06:00
Caleb Gardner ffbf4ebc64 Fuse SUCCESS 2022-12-03 02:45:58 -06:00
Belac Darkstorm a015b16293 Clean path before checking if valid. 2022-10-24 03:17:55 -05:00
Caleb Gardner 327781d86e Fixed issues with fragments 2022-08-26 12:11:27 -05:00
Caleb Gardner 4efd2ee49d Merge pull request #16 from tri-adam/0.6.0-fixes
v0.6.0 fixes
2022-08-26 11:44:16 -05:00
Caleb Gardner 392193993c Added single file test 2022-08-26 11:43:46 -05:00
Adam Hughes 2230a449ec fix: use fs interfaces in type assertions
Previous code would panic due to invalid type assertions (presumably due
to change of type returned by func Sub). Switching to relevant fs
interface types fixes the issue and should work going forward, even if
the type is changed.

Signed-off-by: Adam Hughes <9903835+tri-adam@users.noreply.github.com>
2022-08-26 15:10:51 +00:00
Adam Hughes 0e50efea64 fix: use correct count when reading fragments
Signed-off-by: Adam Hughes <9903835+tri-adam@users.noreply.github.com>
2022-08-26 15:00:00 +00:00
22 changed files with 544 additions and 156 deletions
+1 -3
View File
@@ -17,6 +17,4 @@ Thanks also to [distri's squashfs library](https://github.com/distr1/distri/tree
## Performance ## Performance
This library, decompressing the Firefox AppImage and using go tests, takes about twice as long as `unsquashfs` on my quad core laptop. (~1 second with the library and about half a second with `unsquashfs`). Testing on a zstd compressed file, my library is anywhere from 5x ~ 7x slower then `unsquashfs`
**My recents tests have shown the Firefox AppImage might be an outlier and this library might be considerably slower (4x ~ 6x time slower then `unsquashfs`)**
+123
View File
@@ -0,0 +1,123 @@
package squashfs
import (
"bytes"
"context"
"io"
"github.com/CalebQ42/fuse"
"github.com/CalebQ42/fuse/fs"
"github.com/CalebQ42/squashfs/internal/inode"
)
// Creates a fuse mount, then mounts the archive on a seperate goroutine.
// If waiting for the mount to end, simply do <-con.Ready.
func (r *Reader) Mount(mountpoint string) (con *fuse.Conn, err error) {
con, err = fuse.Mount(mountpoint, fuse.ReadOnly())
if err != nil {
return
}
go fs.Serve(con, &squashFuse{r: r})
return
}
type squashFuse struct {
r *Reader
}
func (s *squashFuse) Root() (fs.Node, error) {
return &fileNode{File: s.r.FS.File}, nil
}
type fileNode struct {
*File
}
func (f *fileNode) Attr(ctx context.Context, attr *fuse.Attr) error {
attr.Blocks = f.r.s.Size / 512
if f.r.s.Size%512 > 0 {
attr.Blocks++
}
attr.Gid = f.r.ids[f.i.GidInd]
attr.Inode = uint64(f.i.Num)
attr.Mode = f.i.Mode()
attr.Nlink = f.i.LinkCount()
attr.Size = f.i.Size()
attr.Uid = f.r.ids[f.i.UidInd]
return nil
}
func (f *fileNode) Id() uint64 {
return uint64(f.i.Num)
}
func (f *fileNode) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
return f.SymlinkPath(), nil
}
func (f *fileNode) Lookup(ctx context.Context, name string) (fs.Node, error) {
asFS, err := f.FS()
if err != nil {
return nil, fuse.ENOTDIR
}
ret, err := asFS.OpenFile(name)
if err != nil {
return nil, fuse.ENOENT
}
return &fileNode{File: ret}, nil
}
func (f *fileNode) ReadAll(ctx context.Context) ([]byte, error) {
if f.IsRegular() {
var buf bytes.Buffer
_, err := f.WriteTo(&buf)
return buf.Bytes(), err
}
return nil, fuse.ENODATA
}
func (f *fileNode) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
if f.IsRegular() {
buf := make([]byte, req.Size)
n, err := f.File.ReadAt(buf, req.Offset)
if err == io.EOF {
resp.Data = buf[:n]
}
return nil
}
return fuse.ENODATA
}
func (f *fileNode) ReadDirAll(ctx context.Context) (out []fuse.Dirent, err error) {
asFS, err := f.FS()
if err != nil {
return nil, fuse.ENOTDIR
}
var t fuse.DirentType
for i := range asFS.e {
switch asFS.e[i].Type {
case inode.Fil:
t = fuse.DT_File
case inode.Dir:
t = fuse.DT_Dir
case inode.Block:
t = fuse.DT_Block
case inode.Sym:
t = fuse.DT_Link
case inode.Char:
t = fuse.DT_Char
case inode.Fifo:
t = fuse.DT_FIFO
case inode.Sock:
t = fuse.DT_Socket
default:
t = fuse.DT_Unknown
}
out = append(out, fuse.Dirent{
Inode: uint64(asFS.e[i].Num),
Type: t,
Name: asFS.e[i].Name,
})
}
return
}
+5 -2
View File
@@ -3,9 +3,12 @@ module github.com/CalebQ42/squashfs
go 1.19 go 1.19
require ( require (
github.com/klauspost/compress v1.15.9 github.com/CalebQ42/fuse v0.1.0
github.com/pierrec/lz4/v4 v4.1.15 github.com/klauspost/compress v1.15.12
github.com/pierrec/lz4/v4 v4.1.17
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e
github.com/therootcompany/xz v1.0.1 github.com/therootcompany/xz v1.0.1
github.com/ulikunitz/xz v0.5.10 github.com/ulikunitz/xz v0.5.10
) )
require golang.org/x/sys v0.2.0 // indirect
+8 -4
View File
@@ -1,10 +1,14 @@
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/CalebQ42/fuse v0.1.0 h1:KLCNjun7zcd2kBNVFfH+SWJyhuwJdE0nhw5/q8K8HGQ=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/CalebQ42/fuse v0.1.0/go.mod h1:pJpoKG03HJKVhsp8o0YQYqmfbFsr3Eowt90yQGQVO+4=
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e h1:dCWirM5F3wMY+cmRda/B1BiPsFtmzXqV9b0hLWtVBMs= github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e h1:dCWirM5F3wMY+cmRda/B1BiPsFtmzXqV9b0hLWtVBMs=
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e/go.mod h1:9leZcVcItj6m9/CfHY5Em/iBrCz7js8LcRQGTKEEv2M= github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e/go.mod h1:9leZcVcItj6m9/CfHY5Em/iBrCz7js8LcRQGTKEEv2M=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+175 -56
View File
@@ -2,6 +2,7 @@ package data
import ( import (
"io" "io"
"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"
@@ -26,9 +27,9 @@ func NewFullReader(r io.ReaderAt, start uint64, d decompress.Decompressor, block
} }
} }
func (r *FullReader) AddFragment(rdr func() (io.Reader, error)) { func (r *FullReader) AddFragment(rdr func() (io.Reader, error), size uint32) {
r.fragRdr = rdr r.fragRdr = rdr
r.sizes = append(r.sizes, 0) r.sizes = append(r.sizes, size)
} }
type outDat struct { type outDat struct {
@@ -37,108 +38,226 @@ type outDat struct {
i int i int
} }
func (r FullReader) process(index int, offset int64, out chan outDat) { func (r FullReader) process(index int, offset int64, od *outDat, out chan *outDat) {
var err error defer func() {
var dat []byte out <- od
var rdr io.ReadCloser }()
od.i = index
size := realSize(r.sizes[index]) size := realSize(r.sizes[index])
if size == 0 { if size == 0 {
out <- outDat{ od.err = nil
i: index, od.data = make([]byte, r.blockSize)
err: nil,
data: make([]byte, r.blockSize),
}
return return
} }
// rdr := io.LimitReader(toreader.NewReader(r.r, offset), int64(size))
if size == r.sizes[index] { if size == r.sizes[index] {
//Special workaround for zstd for increased performancce. if dec, ok := r.d.(decompress.Decoder); ok {
if zstd, ok := r.d.(*decompress.Zstd); ok { dat := make([]byte, size)
dat = make([]byte, size) _, od.err = r.r.ReadAt(dat, offset)
_, err = r.r.ReadAt(dat, offset) if od.err != nil {
if err == nil { return
dat, err = zstd.Decode(dat)
}
} else {
rdr, err = r.d.Reader(io.LimitReader(toreader.NewReader(r.r, offset), int64(size)))
if err == nil {
dat, err = io.ReadAll(rdr)
} }
od.data, od.err = dec.Decode(dat, int(r.blockSize))
return
} }
var rdr io.ReadCloser
rdr, od.err = r.d.Reader(io.LimitReader(toreader.NewReader(r.r, offset), int64(size)))
if od.err != nil {
return
}
od.data = make([]byte, r.blockSize)
var read int
read, od.err = rdr.Read(od.data)
od.data = od.data[:read]
rdr.Close()
} else { } else {
dat = make([]byte, size) od.data = make([]byte, size)
_, err = r.r.ReadAt(dat, offset) _, od.err = r.r.ReadAt(od.data, offset)
}
out <- outDat{
i: index,
err: err,
data: dat,
}
if clr, ok := rdr.(io.Closer); ok {
clr.Close()
} }
} }
func (r FullReader) WriteTo(w io.Writer) (n int64, err error) { func (r FullReader) ReadAt(p []byte, off int64) (n int, err error) {
out := make(chan outDat, len(r.sizes)) pol := &sync.Pool{
New: func() any {
return new(outDat)
},
}
out := make(chan *outDat, len(r.sizes))
offset := r.start offset := r.start
num := len(r.sizes) num := len(r.sizes)
start := off / int64(r.blockSize)
end := len(p) / int(r.blockSize)
if end%int(r.blockSize) > 0 {
end++
}
if end > len(r.sizes) {
if r.fragRdr != nil {
end = len(r.sizes)
} else {
end = len(r.sizes) + 1
}
}
for i := 0; i < num; i++ { for i := 0; i < num; i++ {
if i < int(start) || i > end {
offset += uint64(realSize(r.sizes[i]))
continue
}
od := pol.Get().(*outDat)
if i == num-1 && r.fragRdr != nil { if i == num-1 && r.fragRdr != nil {
go func() { go func() {
defer func() {
out <- od
}()
rdr, e := r.fragRdr() rdr, e := r.fragRdr()
if err != nil { if err != nil {
out <- outDat{ od.i = num - 1
i: num - 1, od.err = e
err: e,
}
return return
} }
dat, e := io.ReadAll(rdr) od.data = make([]byte, r.sizes[num-1])
out <- outDat{ _, e = rdr.Read(od.data)
i: num - 1, od.i = num - 1
err: e, od.err = e
data: dat,
}
if clr, ok := rdr.(io.Closer); ok { if clr, ok := rdr.(io.Closer); ok {
clr.Close() clr.Close()
} }
}() }()
continue continue
} }
go r.process(i, int64(offset), out) go r.process(i, int64(offset), od, out)
offset += uint64(realSize(r.sizes[i])) offset += uint64(realSize(r.sizes[i]))
} }
cur := start
cache := make(map[int]outDat) cache := make(map[int]outDat)
var tmpN int for dat := range out {
for cur := 0; cur < num; {
dat := <-out
if dat.err != nil { if dat.err != nil {
err = dat.err err = dat.err
pol.Put(dat)
return return
} }
if dat.i != cur { if dat.i != int(cur) {
cache[dat.i] = dat cache[dat.i] = *dat
pol.Put(dat)
continue continue
} }
tmpN, err = w.Write(dat.data) if cur == start {
n += int64(tmpN) dat.data = dat.data[off%int64(r.blockSize):]
if err != nil {
return
} }
for i := range dat.data {
p[n+i] = dat.data[i]
}
n += len(dat.data)
cur++ cur++
pol.Put(dat)
var ok bool var ok bool
var curDat outDat
for { for {
dat, ok = cache[cur] curDat, ok = cache[int(cur)]
if !ok { if !ok {
break break
} }
for i := range curDat.data {
p[n+i] = curDat.data[i]
}
n += len(curDat.data)
cur++
delete(cache, int(cur))
}
}
if n < len(p) {
err = io.EOF
}
return
}
func (r FullReader) WriteTo(w io.Writer) (n int64, err error) {
pol := &sync.Pool{
New: func() any {
return new(outDat)
},
}
out := make(chan *outDat, len(r.sizes))
offset := r.start
num := len(r.sizes)
for i := 0; i < num; i++ {
od := pol.Get().(*outDat)
if i == num-1 && r.fragRdr != nil {
go func() {
defer func() {
out <- od
}()
rdr, e := r.fragRdr()
if err != nil {
od.i = num - 1
od.err = e
return
}
buf := make([]byte, r.sizes[num-1])
_, e = rdr.Read(buf)
od.i = num - 1
od.err = e
od.data = buf
if clr, ok := rdr.(io.Closer); ok {
clr.Close()
}
}()
continue
}
go r.process(i, int64(offset), od, out)
offset += uint64(realSize(r.sizes[i]))
}
wt, ok := w.(io.WriterAt)
if !ok {
var cur int
cache := make(map[int]outDat)
var tmpN int
var dat *outDat
for cur < len(r.sizes) {
dat = <-out
defer pol.Put(dat)
if dat.err != nil {
err = dat.err
return
}
if dat.i != cur {
cache[dat.i] = *dat
continue
}
tmpN, err = w.Write(dat.data) tmpN, err = w.Write(dat.data)
n += int64(tmpN) n += int64(tmpN)
if err != nil { if err != nil {
return return
} }
cur++ cur++
var ok bool
var curDat outDat
for {
curDat, ok = cache[cur]
if !ok {
break
}
tmpN, err = w.Write(curDat.data)
n += int64(tmpN)
if err != nil {
return
}
cur++
}
}
} else {
var done int
var dat *outDat
for done < len(r.sizes) {
dat = <-out
defer pol.Put(dat)
if dat.err != nil {
err = dat.err
return
}
_, err = wt.WriteAt(dat.data, int64(dat.i*int(r.blockSize)))
if err != nil {
return
}
done++
} }
} }
return return
+2 -2
View File
@@ -53,14 +53,14 @@ func (r *Reader) advance() (err error) {
} else { } else {
r.cur = io.LimitReader(r.master, int64(size)) r.cur = io.LimitReader(r.master, int64(size))
if size == r.blockSizes[0] { if size == r.blockSizes[0] {
if r.d.Resetable() { if rs, ok := r.d.(decompress.Resetable); ok {
if r.comRdr == nil { if r.comRdr == nil {
r.cur, err = r.d.Reader(r.cur) r.cur, err = r.d.Reader(r.cur)
if err != nil { if err != nil {
return return
} }
} else { } else {
err = r.d.Reset(r.comRdr, r.cur) err = rs.Reset(r.comRdr, r.cur)
r.cur = r.comRdr r.cur = r.comRdr
} }
} else { } else {
-2
View File
@@ -12,8 +12,6 @@ func (g GZip) Reader(src io.Reader) (io.ReadCloser, error) {
return zlib.NewReader(src) return zlib.NewReader(src)
} }
func (g GZip) Resetable() bool { return true }
func (g GZip) Reset(old, src io.Reader) error { func (g GZip) Reset(old, src io.Reader) error {
return old.(zlib.Resetter).Reset(src, nil) return old.(zlib.Resetter).Reset(src, nil)
} }
+8 -5
View File
@@ -1,19 +1,22 @@
package decompress package decompress
import ( import (
"errors"
"io" "io"
) )
var ErrNotResetable = errors.New("decompressor not resetable")
type Decompressor interface { type Decompressor interface {
//Creates a new decompressor reading from src. //Creates a new decompressor reading from src.
Reader(src io.Reader) (io.ReadCloser, error) Reader(src io.Reader) (io.ReadCloser, error)
//Reports whether Reset will work or not. }
Resetable() bool
type Resetable interface {
//Reset attempts to re-use an old decompressor with new data. //Reset attempts to re-use an old decompressor with new data.
//Will return ErrNotResetable if not Resetable(). //Will return ErrNotResetable if not Resetable().
//Must ALWAYS be provided with a reader created with Reader. //Must ALWAYS be provided with a reader created with Reader.
Reset(old, src io.Reader) error Reset(old, src io.Reader) error
} }
type Decoder interface {
//Decodes a chunk of data all at once.
Decode(in []byte, outSize int) ([]byte, error)
}
+9 -2
View File
@@ -12,9 +12,16 @@ func (l Lz4) Reader(r io.Reader) (io.ReadCloser, error) {
return io.NopCloser(lz4.NewReader(r)), nil return io.NopCloser(lz4.NewReader(r)), nil
} }
func (l Lz4) Resetable() bool { return true }
func (l Lz4) Reset(old, src io.Reader) error { func (l Lz4) Reset(old, src io.Reader) error {
old.(*lz4.Reader).Reset(src) old.(*lz4.Reader).Reset(src)
return nil return nil
} }
func (l Lz4) Decode(in []byte, outSize int) (out []byte, err error) {
out = make([]byte, outSize)
outLen, err := lz4.UncompressBlock(in, out)
if outLen < outSize {
out = out[:outLen]
}
return
}
-4
View File
@@ -12,7 +12,3 @@ func (l Lzma) Reader(r io.Reader) (io.ReadCloser, error) {
rdr, err := lzma.NewReader(r) rdr, err := lzma.NewReader(r)
return io.NopCloser(rdr), err return io.NopCloser(rdr), err
} }
func (l Lzma) Resetable() bool { return false }
func (l Lzma) Reset(old, src io.Reader) error { return ErrNotResetable }
-4
View File
@@ -16,7 +16,3 @@ func (l Lzo) Reader(r io.Reader) (io.ReadCloser, error) {
} }
return io.NopCloser(bytes.NewReader(cache)), nil return io.NopCloser(bytes.NewReader(cache)), nil
} }
func (l Lzo) Resetable() bool { return false }
func (l Lzo) Reset(old, src io.Reader) error { return ErrNotResetable }
-2
View File
@@ -13,8 +13,6 @@ func (x Xz) Reader(r io.Reader) (io.ReadCloser, error) {
return io.NopCloser(rdr), err return io.NopCloser(rdr), err
} }
func (x Xz) Resetable() bool { return true }
func (x Xz) Reset(old, src io.Reader) error { func (x Xz) Reset(old, src io.Reader) error {
return old.(*xz.Reader).Reset(src) return old.(*xz.Reader).Reset(src)
} }
+2 -4
View File
@@ -15,15 +15,13 @@ func (z Zstd) Reader(src io.Reader) (io.ReadCloser, error) {
return r.IOReadCloser(), err return r.IOReadCloser(), err
} }
func (z Zstd) Resetable() bool { return true }
func (z Zstd) Reset(old, src io.Reader) error { func (z Zstd) Reset(old, src io.Reader) error {
return old.(*zstd.Decoder).Reset(src) return old.(*zstd.Decoder).Reset(src)
} }
func (z *Zstd) Decode(in []byte) (out []byte, err error) { func (z Zstd) Decode(in []byte, outSize int) ([]byte, error) {
if z.writeToReader == nil { if z.writeToReader == nil {
z.writeToReader, _ = zstd.NewReader(nil) z.writeToReader, _ = zstd.NewReader(nil)
} }
return z.writeToReader.DecodeAll(in, nil) return z.writeToReader.DecodeAll(in, make([]byte, outSize))
} }
+2
View File
@@ -29,6 +29,7 @@ type Entry struct {
BlockStart uint32 BlockStart uint32
Type uint16 Type uint16
Offset uint16 Offset uint16
Num uint32
} }
func readEntry(r io.Reader) (e entry, err error) { func readEntry(r io.Reader) (e entry, err error) {
@@ -72,6 +73,7 @@ func ReadEntries(rdr io.Reader, size uint32) (e []Entry, err error) {
BlockStart: h.InodeStart, BlockStart: h.InodeStart,
Type: en.Type, Type: en.Type,
Offset: en.Offset, Offset: en.Offset,
Num: h.Num + uint32(en.NumOffset),
}) })
} }
} }
+2 -2
View File
@@ -9,7 +9,7 @@ import (
type fileInit struct { type fileInit struct {
BlockStart uint32 BlockStart uint32
FragInd uint32 FragInd uint32
Offset uint32 FragOffset uint32
Size uint32 Size uint32
} }
@@ -24,7 +24,7 @@ type eFileInit struct {
Sparse uint64 Sparse uint64
LinkCount uint32 LinkCount uint32
FragInd uint32 FragInd uint32
Offset uint32 FragOffset uint32
XattrInd uint32 XattrInd uint32
} }
+64
View File
@@ -4,6 +4,7 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"io" "io"
"io/fs"
"strconv" "strconv"
) )
@@ -77,3 +78,66 @@ func Read(r io.Reader, blockSize uint32) (i Inode, err error) {
} }
return return
} }
func (i Inode) Mode() (out fs.FileMode) {
out = fs.FileMode(i.Perm)
switch i.Data.(type) {
case Directory:
out |= fs.ModeDir
case EDirectory:
out |= fs.ModeDir
case Symlink:
out |= fs.ModeSymlink
case ESymlink:
out |= fs.ModeSymlink
case Device:
out |= fs.ModeDevice
case EDevice:
out |= fs.ModeDevice
case IPC:
out |= fs.ModeNamedPipe
case EIPC:
out |= fs.ModeNamedPipe
}
return
}
func (i Inode) LinkCount() uint32 {
switch i.Data.(type) {
case EFile:
return i.Data.(EFile).LinkCount
case Directory:
return i.Data.(Directory).LinkCount
case EDirectory:
return i.Data.(EDirectory).LinkCount
case Device:
return i.Data.(Device).LinkCount
case EDevice:
return i.Data.(EDevice).LinkCount
case IPC:
return i.Data.(IPC).LinkCount
case EIPC:
return i.Data.(EIPC).LinkCount
case Symlink:
return i.Data.(Symlink).LinkCount
case ESymlink:
return i.Data.(ESymlink).LinkCount
default:
return 0
}
}
func (i Inode) Size() uint64 {
switch i.Data.(type) {
case File:
return uint64(i.Data.(File).Size)
case EFile:
return i.Data.(EFile).Size
// case Directory:
// return uint64(i.Data.(Directory).Size)
// case EDirectory:
// return uint64(i.Data.(EDirectory).Size)
default:
return 0
}
}
+3 -3
View File
@@ -26,7 +26,7 @@ func realSize(siz uint16) uint16 {
} }
func (r *Reader) advance() (err error) { func (r *Reader) advance() (err error) {
if !r.d.Resetable() { if _, ok := r.d.(decompress.Resetable); !ok {
if clr, ok := r.cur.(io.Closer); ok { if clr, ok := r.cur.(io.Closer); ok {
clr.Close() clr.Close()
} }
@@ -39,14 +39,14 @@ func (r *Reader) advance() (err error) {
size := realSize(raw) size := realSize(raw)
r.cur = io.LimitReader(r.master, int64(size)) r.cur = io.LimitReader(r.master, int64(size))
if size == raw { if size == raw {
if r.d.Resetable() { if rs, ok := r.d.(decompress.Resetable); ok {
if r.comRdr == nil { if r.comRdr == nil {
r.cur, err = r.d.Reader(r.cur) r.cur, err = r.d.Reader(r.cur)
if err != nil { if err != nil {
return return
} }
} else { } else {
err = r.d.Reset(r.comRdr, r.cur) err = rs.Reset(r.comRdr, r.cur)
r.cur = r.comRdr r.cur = r.comRdr
} }
} else { } else {
+4 -4
View File
@@ -96,20 +96,20 @@ func NewReader(r io.ReaderAt) (*Reader, error) {
return nil, err return nil, err
} }
} else { } else {
toRead := squash.s.IdCount toRead := squash.s.FragCount
var curRead uint16 var curRead uint32
var tmp []fragEntry var tmp []fragEntry
var rdr *metadata.Reader var rdr *metadata.Reader
var offset int var offset int
for i := range fragOffsets { for i := range fragOffsets {
curRead = uint16(math.Min(512, float64(toRead))) curRead = uint32(math.Min(512, float64(toRead)))
tmp = make([]fragEntry, curRead) tmp = make([]fragEntry, curRead)
rdr = metadata.NewReader(toreader.NewReader(r, int64(fragOffsets[i])), squash.d) rdr = metadata.NewReader(toreader.NewReader(r, int64(fragOffsets[i])), squash.d)
err = binary.Read(rdr, binary.LittleEndian, &tmp) err = binary.Read(rdr, binary.LittleEndian, &tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
offset = int(squash.s.IdCount - toRead) offset = int(squash.s.FragCount - toRead)
for i := range tmp { for i := range tmp {
squash.fragEntries[offset+i] = tmp[i] squash.fragEntries[offset+i] = tmp[i]
} }
+31 -26
View File
@@ -10,15 +10,16 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/CalebQ42/squashfs/internal/data"
"github.com/CalebQ42/squashfs/internal/directory" "github.com/CalebQ42/squashfs/internal/directory"
"github.com/CalebQ42/squashfs/internal/inode" "github.com/CalebQ42/squashfs/internal/inode"
) )
//File represents a file inside a squashfs archive. // File represents a file inside a squashfs archive.
type File struct { type File struct {
i inode.Inode i inode.Inode
rdr io.Reader rdr io.Reader
fullRdr io.WriterTo fullRdr *data.FullReader
r *Reader r *Reader
parent *FS parent *FS
e directory.Entry e directory.Entry
@@ -35,7 +36,7 @@ func (r Reader) newFile(en directory.Entry, parent *FS) (*File, error) {
return nil, err return nil, err
} }
var rdr io.Reader var rdr io.Reader
var full io.WriterTo var full *data.FullReader
if i.Type == inode.Fil || i.Type == inode.EFil { if i.Type == inode.Fil || i.Type == inode.EFil {
full, rdr, err = r.getReaders(i) full, rdr, err = r.getReaders(i)
if err != nil { if err != nil {
@@ -52,12 +53,12 @@ func (r Reader) newFile(en directory.Entry, parent *FS) (*File, error) {
}, nil }, nil
} }
//Stat returns the File's fs.FileInfo // Stat returns the File's fs.FileInfo
func (f File) Stat() (fs.FileInfo, error) { func (f File) Stat() (fs.FileInfo, error) {
return newFileInfo(f.e, f.i), nil return newFileInfo(f.e, f.i), nil
} }
//Read reads the data from the file. Only works if file is a normal file. // Read reads the data from the file. Only works if file is a normal file.
func (f File) Read(p []byte) (int, error) { func (f File) Read(p []byte) (int, error) {
if f.i.Type != inode.Fil && f.i.Type != inode.EFil { if f.i.Type != inode.Fil && f.i.Type != inode.EFil {
return 0, ErrReadNotFile return 0, ErrReadNotFile
@@ -68,20 +69,24 @@ func (f File) Read(p []byte) (int, error) {
return f.rdr.Read(p) return f.rdr.Read(p)
} }
//WriteTo writes all data from the file to the writer. This is multi-threaded. func (f File) ReadAt(p []byte, off int64) (int, error) {
//The underlying reader is seperate from the one used with Read and can be reused. return f.fullRdr.ReadAt(p, off)
}
// WriteTo writes all data from the file to the writer. This is multi-threaded.
// The underlying reader is seperate from the one used with Read and can be reused.
func (f File) WriteTo(w io.Writer) (int64, error) { func (f File) WriteTo(w io.Writer) (int64, error) {
return f.fullRdr.WriteTo(w) return f.fullRdr.WriteTo(w)
} }
//Close simply nils the underlying reader. Here mostly to satisfy fs.File // Close simply nils the underlying reader. Here mostly to satisfy fs.File
func (f *File) Close() error { func (f *File) Close() error {
f.rdr = nil f.rdr = nil
return nil return nil
} }
//ReadDir returns n fs.DirEntry's that's contained in the File (if it's a directory). // ReadDir returns n fs.DirEntry's that's contained in the File (if it's a directory).
//If n <= 0 all fs.DirEntry's are returned. // If n <= 0 all fs.DirEntry's are returned.
func (f *File) ReadDir(n int) (out []fs.DirEntry, err error) { func (f *File) ReadDir(n int) (out []fs.DirEntry, err error) {
if !f.IsDir() { if !f.IsDir() {
return nil, errors.New("File is not a directory") return nil, errors.New("File is not a directory")
@@ -111,7 +116,7 @@ func (f *File) ReadDir(n int) (out []fs.DirEntry, err error) {
return return
} }
//FS returns the File as a FS. // FS returns the File as a FS.
func (f *File) FS() (*FS, error) { func (f *File) FS() (*FS, error) {
if !f.IsDir() { if !f.IsDir() {
return nil, errors.New("File is not a directory") return nil, errors.New("File is not a directory")
@@ -126,22 +131,22 @@ func (f *File) FS() (*FS, error) {
}, nil }, nil
} }
//IsDir Yep. // IsDir Yep.
func (f File) IsDir() bool { func (f File) IsDir() bool {
return f.i.Type == inode.Dir || f.i.Type == inode.EDir return f.i.Type == inode.Dir || f.i.Type == inode.EDir
} }
//IsRegular yep. // IsRegular yep.
func (f File) IsRegular() bool { func (f File) IsRegular() bool {
return f.i.Type == inode.Fil || f.i.Type == inode.EFil return f.i.Type == inode.Fil || f.i.Type == inode.EFil
} }
//IsSymlink yep. // IsSymlink yep.
func (f File) IsSymlink() bool { func (f File) IsSymlink() bool {
return f.i.Type == inode.Sym || f.i.Type == inode.ESym return f.i.Type == inode.Sym || f.i.Type == inode.ESym
} }
//SymlinkPath returns the symlink's target path. Is the File isn't a symlink, returns an empty string. // SymlinkPath returns the symlink's target path. Is the File isn't a symlink, returns an empty string.
func (f File) SymlinkPath() string { func (f File) SymlinkPath() string {
switch f.i.Type { switch f.i.Type {
case inode.Sym: case inode.Sym:
@@ -159,8 +164,8 @@ func (f File) path() string {
return f.parent.path() + "/" + f.e.Name return f.parent.path() + "/" + f.e.Name
} }
//GetSymlinkFile returns the File the symlink is pointing to. // GetSymlinkFile returns the File the symlink is pointing to.
//If not a symlink, or the target is unobtainable (such as it being outside the archive or it's absolute) returns nil // If not a symlink, or the target is unobtainable (such as it being outside the archive or it's absolute) returns nil
func (f File) GetSymlinkFile() *File { func (f File) GetSymlinkFile() *File {
if !f.IsSymlink() { if !f.IsSymlink() {
return nil return nil
@@ -175,7 +180,7 @@ func (f File) GetSymlinkFile() *File {
return sym.(*File) return sym.(*File)
} }
//ExtractionOptions are available options on how to extract. // ExtractionOptions are available options on how to extract.
type ExtractionOptions struct { type ExtractionOptions struct {
LogOutput io.Writer //Where error log should write. If nil, uses os.Stdout. Has no effect if verbose is false. LogOutput io.Writer //Where error log should write. If nil, uses os.Stdout. Has no effect if verbose is false.
DereferenceSymlink bool //Replace symlinks with the target file DereferenceSymlink bool //Replace symlinks with the target file
@@ -184,21 +189,21 @@ type ExtractionOptions struct {
FolderPerm fs.FileMode //The permissions used when creating the extraction folder FolderPerm fs.FileMode //The permissions used when creating the extraction folder
} }
//DefaultOptions is the default ExtractionOptions. // DefaultOptions is the default ExtractionOptions.
func DefaultOptions() ExtractionOptions { func DefaultOptions() ExtractionOptions {
return ExtractionOptions{ return ExtractionOptions{
FolderPerm: 0755, FolderPerm: 0755,
} }
} }
//ExtractTo extracts the File to the given folder with the default options. // ExtractTo extracts the File to the given folder with the default options.
//If the File is a directory, it instead extracts the directory's contents to the folder. // If the File is a directory, it instead extracts the directory's contents to the folder.
func (f File) ExtractTo(folder string) error { func (f File) ExtractTo(folder string) error {
return f.ExtractWithOptions(folder, DefaultOptions()) return f.ExtractWithOptions(folder, DefaultOptions())
} }
//ExtractSymlink extracts the File to the folder with the DereferenceSymlink option. // ExtractSymlink extracts the File to the folder with the DereferenceSymlink option.
//If the File is a directory, it instead extracts the directory's contents to the folder. // If the File is a directory, it instead extracts the directory's contents to the folder.
func (f File) ExtractSymlink(folder string) error { func (f File) ExtractSymlink(folder string) error {
return f.ExtractWithOptions(folder, ExtractionOptions{ return f.ExtractWithOptions(folder, ExtractionOptions{
DereferenceSymlink: true, DereferenceSymlink: true,
@@ -206,8 +211,8 @@ func (f File) ExtractSymlink(folder string) error {
}) })
} }
//ExtractWithOptions extracts the File to the given folder with the given ExtrationOptions. // ExtractWithOptions extracts the File to the given folder with the given ExtrationOptions.
//If the File is a directory, it instead extracts the directory's contents to the folder. // If the File is a directory, it instead extracts the directory's contents to the folder.
func (f File) ExtractWithOptions(folder string, op ExtractionOptions) error { func (f File) ExtractWithOptions(folder string, op ExtractionOptions) error {
if op.Verbose { if op.Verbose {
if op.LogOutput == nil { if op.LogOutput == nil {
@@ -220,13 +225,13 @@ func (f File) ExtractWithOptions(folder string, op ExtractionOptions) error {
func (f File) realExtract(folder string, op ExtractionOptions) error { func (f File) realExtract(folder string, op ExtractionOptions) error {
err := os.MkdirAll(folder, op.FolderPerm) err := os.MkdirAll(folder, op.FolderPerm)
folder = filepath.Clean(folder)
if err != nil && !os.IsExist(err) { if err != nil && !os.IsExist(err) {
if op.Verbose { if op.Verbose {
log.Println("Error while creating extraction folder") log.Println("Error while creating extraction folder")
} }
return err return err
} }
folder = filepath.Clean(folder)
if f.IsDir() { if f.IsDir() {
filFS, _ := f.FS() filFS, _ := f.FS()
var ents []directory.Entry var ents []directory.Entry
+26 -21
View File
@@ -12,8 +12,8 @@ import (
"github.com/CalebQ42/squashfs/internal/inode" "github.com/CalebQ42/squashfs/internal/inode"
) )
//FS is a fs.FS representation of a squashfs directory. // FS is a fs.FS representation of a squashfs directory.
//Implements fs.GlobFS, fs.ReadDirFS, fs.ReadFileFS, fs.StatFS, and fs.SubFS // Implements fs.GlobFS, fs.ReadDirFS, fs.ReadFileFS, fs.StatFS, and fs.SubFS
type FS struct { type FS struct {
*File *File
e []directory.Entry e []directory.Entry
@@ -39,8 +39,9 @@ func (r Reader) newFS(e directory.Entry, parent *FS) (*FS, error) {
}, nil }, nil
} }
//Open opens the file at name. Returns a squashfs.File. // Opens the file at name. Returns a squashfs.File.
func (f FS) Open(name string) (fs.File, error) { func (f FS) OpenFile(name string) (*File, error) {
name = filepath.Clean(name)
if !fs.ValidPath(name) { if !fs.ValidPath(name) {
return nil, &fs.PathError{ return nil, &fs.PathError{
Op: "open", Op: "open",
@@ -48,7 +49,6 @@ func (f FS) Open(name string) (fs.File, error) {
Err: fs.ErrInvalid, Err: fs.ErrInvalid,
} }
} }
name = filepath.Clean(name)
if name == "." || name == "" { if name == "." || name == "" {
return f.File, nil return f.File, nil
} }
@@ -73,7 +73,7 @@ func (f FS) Open(name string) (fs.File, error) {
Err: err, Err: err,
} }
} }
out, err := newFS.Open(strings.Join(split[1:], "/")) out, err := newFS.OpenFile(strings.Join(split[1:], "/"))
if err != nil { if err != nil {
err.(*fs.PathError).Path = name err.(*fs.PathError).Path = name
} }
@@ -96,10 +96,16 @@ func (f FS) Open(name string) (fs.File, error) {
} }
} }
//Glob returns the name of the files at the given pattern. // Opens the file at name. Returns a io/fs.File.
//All paths are relative to the FS. func (f FS) Open(name string) (fs.File, error) {
//Uses filepath.Match to compare names. return f.OpenFile(name)
}
// Glob returns the name of the files at the given pattern.
// All paths are relative to the FS.
// Uses filepath.Match to compare names.
func (f FS) Glob(pattern string) (out []string, err error) { func (f FS) Glob(pattern string) (out []string, err error) {
pattern = filepath.Clean(pattern)
if !fs.ValidPath(pattern) { if !fs.ValidPath(pattern) {
return nil, &fs.PathError{ return nil, &fs.PathError{
Op: "glob", Op: "glob",
@@ -107,7 +113,6 @@ func (f FS) Glob(pattern string) (out []string, err error) {
Err: fs.ErrInvalid, Err: fs.ErrInvalid,
} }
} }
pattern = filepath.Clean(pattern)
split := strings.Split(pattern, "/") split := strings.Split(pattern, "/")
for i := 0; i < len(f.e); i++ { for i := 0; i < len(f.e); i++ {
if match, _ := path.Match(split[0], f.e[i].Name); match { if match, _ := path.Match(split[0], f.e[i].Name); match {
@@ -131,7 +136,7 @@ func (f FS) Glob(pattern string) (out []string, err error) {
Err: err, Err: err,
} }
} }
subGlob, err := sub.(FS).Glob(strings.Join(split[1:], "/")) subGlob, err := sub.(fs.GlobFS).Glob(strings.Join(split[1:], "/"))
if err != nil { if err != nil {
if pathErr, ok := err.(*fs.PathError); ok { if pathErr, ok := err.(*fs.PathError); ok {
if pathErr.Err == fs.ErrNotExist { if pathErr.Err == fs.ErrNotExist {
@@ -156,9 +161,10 @@ func (f FS) Glob(pattern string) (out []string, err error) {
return return
} }
//ReadDir returns all the DirEntry returns all DirEntry's for the directory at name. // ReadDir returns all the DirEntry returns all DirEntry's for the directory at name.
//If name is not a directory, returns an error. // If name is not a directory, returns an error.
func (f FS) ReadDir(name string) ([]fs.DirEntry, error) { func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
name = filepath.Clean(name)
if !fs.ValidPath(name) { if !fs.ValidPath(name) {
return nil, &fs.PathError{ return nil, &fs.PathError{
Op: "readdir", Op: "readdir",
@@ -166,7 +172,6 @@ func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
Err: fs.ErrInvalid, Err: fs.ErrInvalid,
} }
} }
name = filepath.Clean(name)
if name == "." || name == "" { if name == "." || name == "" {
return f.File.ReadDir(-1) return f.File.ReadDir(-1)
} }
@@ -208,7 +213,7 @@ func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
Err: err, Err: err,
} }
} }
redDir, err := sub.(FS).ReadDir(strings.Join(split[1:], "/")) redDir, err := sub.(fs.ReadDirFS).ReadDir(strings.Join(split[1:], "/"))
if err != nil { if err != nil {
if pathErr, ok := err.(*fs.PathError); ok { if pathErr, ok := err.(*fs.PathError); ok {
if pathErr.Err == fs.ErrNotExist { if pathErr.Err == fs.ErrNotExist {
@@ -234,7 +239,7 @@ func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
} }
} }
//ReadFile returns the data (in []byte) for the file at name. // ReadFile returns the data (in []byte) for the file at name.
func (f FS) ReadFile(name string) ([]byte, error) { func (f FS) ReadFile(name string) ([]byte, error) {
fil, err := f.Open(name) fil, err := f.Open(name)
if err != nil { if err != nil {
@@ -256,8 +261,9 @@ func (f FS) ReadFile(name string) ([]byte, error) {
return buf.Bytes(), nil return buf.Bytes(), nil
} }
//Stat returns the fs.FileInfo for the file at name. // Stat returns the fs.FileInfo for the file at name.
func (f FS) Stat(name string) (fs.FileInfo, error) { func (f FS) Stat(name string) (fs.FileInfo, error) {
name = filepath.Clean(strings.TrimPrefix(name, "/"))
if !fs.ValidPath(name) { if !fs.ValidPath(name) {
return nil, &fs.PathError{ return nil, &fs.PathError{
Op: "stat", Op: "stat",
@@ -265,7 +271,6 @@ func (f FS) Stat(name string) (fs.FileInfo, error) {
Err: fs.ErrInvalid, Err: fs.ErrInvalid,
} }
} }
name = filepath.Clean(strings.TrimPrefix(name, "/"))
if name == "." || name == "" { if name == "." || name == "" {
return f.File.Stat() return f.File.Stat()
} }
@@ -299,7 +304,7 @@ func (f FS) Stat(name string) (fs.FileInfo, error) {
Err: err, Err: err,
} }
} }
stat, err := sub.(FS).Stat(strings.Join(split[1:], "/")) stat, err := sub.(fs.StatFS).Stat(strings.Join(split[1:], "/"))
if err != nil { if err != nil {
if pathErr, ok := err.(*fs.PathError); ok { if pathErr, ok := err.(*fs.PathError); ok {
if pathErr.Err == fs.ErrNotExist { if pathErr.Err == fs.ErrNotExist {
@@ -325,8 +330,9 @@ func (f FS) Stat(name string) (fs.FileInfo, error) {
} }
} }
//Sub returns the FS at dir // Sub returns the FS at dir
func (f FS) Sub(dir string) (fs.FS, error) { func (f FS) Sub(dir string) (fs.FS, error) {
dir = filepath.Clean(dir)
if !fs.ValidPath(dir) { if !fs.ValidPath(dir) {
return nil, &fs.PathError{ return nil, &fs.PathError{
Op: "sub", Op: "sub",
@@ -334,7 +340,6 @@ func (f FS) Sub(dir string) (fs.FS, error) {
Err: fs.ErrInvalid, Err: fs.ErrInvalid,
} }
} }
dir = filepath.Clean(dir)
if dir == "." || dir == "" { if dir == "." || dir == "" {
return f, nil return f, nil
} }
+17 -9
View File
@@ -37,13 +37,13 @@ func (r Reader) getReaders(i inode.Inode) (full *data.FullReader, rdr *data.Read
var fragInd uint32 var fragInd uint32
var fragSize uint32 var fragSize uint32
if i.Type == inode.Fil { if i.Type == inode.Fil {
fragOffset = uint64(i.Data.(inode.File).Offset) fragOffset = uint64(i.Data.(inode.File).FragOffset)
blockOffset = uint64(i.Data.(inode.File).BlockStart) blockOffset = uint64(i.Data.(inode.File).BlockStart)
blockSizes = i.Data.(inode.File).BlockSizes blockSizes = i.Data.(inode.File).BlockSizes
fragInd = i.Data.(inode.File).FragInd fragInd = i.Data.(inode.File).FragInd
fragSize = i.Data.(inode.File).Size % r.s.BlockSize fragSize = i.Data.(inode.File).Size % r.s.BlockSize
} else if i.Type == inode.EFil { } else if i.Type == inode.EFil {
fragOffset = uint64(i.Data.(inode.EFile).Offset) fragOffset = uint64(i.Data.(inode.EFile).FragOffset)
blockOffset = i.Data.(inode.EFile).BlockStart blockOffset = i.Data.(inode.EFile).BlockStart
blockSizes = i.Data.(inode.EFile).BlockSizes blockSizes = i.Data.(inode.EFile).BlockSizes
fragInd = i.Data.(inode.EFile).FragInd fragInd = i.Data.(inode.EFile).FragInd
@@ -60,21 +60,29 @@ func (r Reader) getReaders(i inode.Inode) (full *data.FullReader, rdr *data.Read
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, err = fragRdr.Read(make([]byte, fragOffset)) var n, tmpN int
if err != nil { for n != int(fragOffset) {
return nil, err tmpN, err = fragRdr.Read(make([]byte, int(fragOffset)-n))
if err != nil {
return nil, err
}
n += tmpN
} }
fragRdr = io.LimitReader(fragRdr, int64(fragSize)) fragRdr = io.LimitReader(fragRdr, int64(fragSize))
return fragRdr, nil return fragRdr, nil
}) }, fragSize)
var fragRdr io.Reader var fragRdr io.Reader
fragRdr, err = r.fragReader(fragInd) fragRdr, err = r.fragReader(fragInd)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
_, err = fragRdr.Read(make([]byte, fragOffset)) var n, tmpN int
if err != nil { for n != int(fragOffset) {
return nil, nil, err tmpN, err = fragRdr.Read(make([]byte, int(fragOffset)-n))
if err != nil {
return nil, nil, err
}
n += tmpN
} }
fragRdr = io.LimitReader(fragRdr, int64(fragSize)) fragRdr = io.LimitReader(fragRdr, int64(fragSize))
rdr.AddFragment(fragRdr) rdr.AddFragment(fragRdr)
+62 -1
View File
@@ -20,6 +20,8 @@ import (
const ( const (
squashfsURL = "https://darkstorm.tech/LinuxPATest.sfs" squashfsURL = "https://darkstorm.tech/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) {
@@ -55,8 +57,22 @@ func preTest(dir string) (fil *os.File, err error) {
return return
} }
func TestMisc(t *testing.T) {
tmpDir := "testing"
fil, err := preTest(tmpDir)
if err != nil {
t.Fatal(err)
}
rdr, err := squashfs.NewReader(fil)
if err != nil {
t.Fatal(err)
}
_ = rdr
// Put testing here
t.Fatal("UM")
}
func BenchmarkRace(b *testing.B) { func BenchmarkRace(b *testing.B) {
// tmpDir := b.TempDir()
tmpDir := "testing" tmpDir := "testing"
fil, err := preTest(tmpDir) fil, err := preTest(tmpDir)
if err != nil { if err != nil {
@@ -142,4 +158,49 @@ func TestExtractQuick(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Fatal("end")
}
func TestSingleFile(t *testing.T) {
tmpDir := "testing"
fil, err := preTest(tmpDir)
if err != nil {
t.Fatal(err)
}
os.Remove(filepath.Base(filePath))
rdr, err := squashfs.NewReader(fil)
if err != nil {
t.Fatal(err)
}
f, err := rdr.Open(filePath)
if err != nil {
t.Fatal(err)
}
op := squashfs.DefaultOptions()
op.Verbose = true
err = f.(*squashfs.File).ExtractWithOptions("testing", op)
if err != nil {
t.Fatal(err)
}
t.Fatal("HI")
}
func TestFuse(t *testing.T) {
tmpDir := "testing"
fil, err := preTest(tmpDir)
if err != nil {
t.Fatal(err)
}
os.Remove(filepath.Base(filePath))
rdr, err := squashfs.NewReader(fil)
if err != nil {
t.Fatal(err)
}
con, err := rdr.Mount("testing/fuseTest")
if err != nil {
t.Fatal(err)
}
defer con.Close()
<-con.Ready
t.Fatal("testing")
} }