From d4d1b2c2b2a961da97b7b3dc1357fa82427c28d1 Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Tue, 19 Dec 2023 03:23:24 -0600 Subject: [PATCH] Reset to zero --- cmd/go-unsquashfs/main.go | 37 --- fuse2.go | 150 --------- fuse3.go | 148 --------- fuse_darwin.go | 7 - fuse_linux.go | 5 - fuse_windows.go | 3 - go.mod | 14 - go.sum | 16 - internal/data/fullreader.go | 235 -------------- internal/data/reader.go | 104 ------ internal/decompress/gzip.go | 17 - internal/decompress/interface.go | 22 -- internal/decompress/lz4.go | 18 -- internal/decompress/lzma.go | 14 - internal/decompress/lzo.go | 18 -- internal/decompress/xz.go | 18 -- internal/decompress/zstd.go | 27 -- internal/directory/directory.go | 80 ----- internal/inode/dir.go | 65 ---- internal/inode/file.go | 62 ---- internal/inode/inode.go | 143 --------- internal/inode/misc.go | 45 --- internal/inode/sym.go | 46 --- internal/metadata/reader.go | 81 ----- internal/threadmanager/manager.go | 23 -- internal/toreader/offsetreader.go | 19 -- internal/toreader/reader.go | 25 -- internal/toreader/readerat.go | 24 -- reader.go | 234 -------------- reader_file.go | 518 ------------------------------ reader_fileinfo.go | 66 ---- reader_frag.go | 26 -- reader_fs.go | 380 ---------------------- reader_inode.go | 117 ------- squashfs_test.go | 207 ------------ 35 files changed, 3014 deletions(-) delete mode 100644 cmd/go-unsquashfs/main.go delete mode 100644 fuse2.go delete mode 100644 fuse3.go delete mode 100644 fuse_darwin.go delete mode 100644 fuse_linux.go delete mode 100644 fuse_windows.go delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 internal/data/fullreader.go delete mode 100644 internal/data/reader.go delete mode 100644 internal/decompress/gzip.go delete mode 100644 internal/decompress/interface.go delete mode 100644 internal/decompress/lz4.go delete mode 100644 internal/decompress/lzma.go delete mode 100644 internal/decompress/lzo.go delete mode 100644 internal/decompress/xz.go delete mode 100644 internal/decompress/zstd.go delete mode 100644 internal/directory/directory.go delete mode 100644 internal/inode/dir.go delete mode 100644 internal/inode/file.go delete mode 100644 internal/inode/inode.go delete mode 100644 internal/inode/misc.go delete mode 100644 internal/inode/sym.go delete mode 100644 internal/metadata/reader.go delete mode 100644 internal/threadmanager/manager.go delete mode 100644 internal/toreader/offsetreader.go delete mode 100644 internal/toreader/reader.go delete mode 100644 internal/toreader/readerat.go delete mode 100644 reader.go delete mode 100644 reader_file.go delete mode 100644 reader_fileinfo.go delete mode 100644 reader_frag.go delete mode 100644 reader_fs.go delete mode 100644 reader_inode.go delete mode 100644 squashfs_test.go diff --git a/cmd/go-unsquashfs/main.go b/cmd/go-unsquashfs/main.go deleted file mode 100644 index 75be53f..0000000 --- a/cmd/go-unsquashfs/main.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" - "time" - - "github.com/CalebQ42/squashfs" -) - -func main() { - verbose := flag.Bool("v", false, "Verbose") - ignore := flag.Bool("ip", false, "Ignore Permissions and extract all files/folders with 0755") - flag.Parse() - if len(flag.Args()) < 2 { - fmt.Println("Please provide a file name and extraction path") - os.Exit(0) - } - f, err := os.Open(flag.Arg(0)) - if err != nil { - panic(err) - } - r, err := squashfs.NewReader(f) - if err != nil { - panic(err) - } - op := squashfs.DefaultOptions() - op.Verbose = *verbose - op.IgnorePerm = *ignore - n := time.Now() - err = r.ExtractWithOptions(flag.Arg(1), op) - if err != nil { - panic(err) - } - fmt.Println("Took:", time.Since(n)) -} diff --git a/fuse2.go b/fuse2.go deleted file mode 100644 index 8338610..0000000 --- a/fuse2.go +++ /dev/null @@ -1,150 +0,0 @@ -package squashfs - -import ( - "bytes" - "context" - "errors" - "io" - - "github.com/CalebQ42/squashfs/internal/inode" - "github.com/seaweedfs/fuse" - "github.com/seaweedfs/fuse/fs" -) - -// Mounts the archive to the given mountpoint using fuse2. Non-blocking. -// If Unmount does not get called, the mount point must be unmounted using umount before the directory can be used again. -func (r *Reader) MountFuse2(mountpoint string) (err error) { - if r.con != nil { - return errors.New("squashfs archive already mounted") - } - r.con2, err = fuse.Mount(mountpoint, fuse.ReadOnly()) - if err != nil { - return - } - <-r.con2.Ready - r.mount2Done = make(chan struct{}) - go func() { - fs.Serve(r.con2, squashFuse2{r: r}) - close(r.mount2Done) - }() - return -} - -// Blocks until the mount ends. -// Fuse2 version. -func (r *Reader) MountWaitFuse2() { - if r.mount2Done != nil { - <-r.mount2Done - } -} - -// Unmounts the archive. -// Fuse2 version. -func (r *Reader) UnmountFuse2() error { - if r.con != nil { - defer func() { r.con = nil }() - return r.con.Close() - } - return errors.New("squashfs archive is not mounted") -} - -type squashFuse2 struct { - r *Reader -} - -func (s squashFuse2) Root() (fs.Node, error) { - return fileNode2{File: s.r.FS.File}, nil -} - -type fileNode2 struct { - *File -} - -func (f fileNode2) 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 fileNode2) Id() uint64 { - return uint64(f.i.Num) -} - -func (f fileNode2) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) { - return f.SymlinkPath(), nil -} - -func (f fileNode2) 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 fileNode2{File: ret}, nil -} - -func (f fileNode2) ReadAll(ctx context.Context) ([]byte, error) { - if f.IsRegular() { - var buf bytes.Buffer - _, err := f.WriteTo(&buf) - return buf.Bytes(), err - } - return nil, ENODATA -} - -func (f fileNode2) 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 ENODATA -} - -func (f fileNode2) 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 -} diff --git a/fuse3.go b/fuse3.go deleted file mode 100644 index e0b8b28..0000000 --- a/fuse3.go +++ /dev/null @@ -1,148 +0,0 @@ -package squashfs - -import ( - "bytes" - "context" - "errors" - "io" - - "github.com/CalebQ42/fuse" - "github.com/CalebQ42/fuse/fs" - "github.com/CalebQ42/squashfs/internal/inode" -) - -// Mounts the archive to the given mountpoint using fuse3. Non-blocking. -// If Unmount does not get called, the mount point must be unmounted using umount before the directory can be used again. -func (r *Reader) Mount(mountpoint string) (err error) { - if r.con != nil { - return errors.New("squashfs archive already mounted") - } - r.con, err = fuse.Mount(mountpoint, fuse.ReadOnly()) - if err != nil { - return - } - <-r.con.Ready - r.mountDone = make(chan struct{}) - go func() { - fs.Serve(r.con, squashFuse{r: r}) - close(r.mountDone) - }() - return -} - -// Blocks until the mount ends. -func (r *Reader) MountWait() { - if r.mountDone != nil { - <-r.mountDone - } -} - -// Unmounts the archive. -func (r *Reader) Unmount() error { - if r.con != nil { - defer func() { r.con = nil }() - return r.con.Close() - } - return errors.New("squashfs archive is not mounted") -} - -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, 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 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 -} diff --git a/fuse_darwin.go b/fuse_darwin.go deleted file mode 100644 index 353aee1..0000000 --- a/fuse_darwin.go +++ /dev/null @@ -1,7 +0,0 @@ -package squashfs - -import ( - "golang.org/x/sys/unix" -) - -var ENODATA = unix.Errno(unix.ENODATA) diff --git a/fuse_linux.go b/fuse_linux.go deleted file mode 100644 index 816560a..0000000 --- a/fuse_linux.go +++ /dev/null @@ -1,5 +0,0 @@ -package squashfs - -import "github.com/CalebQ42/fuse" - -var ENODATA = fuse.ENODATA diff --git a/fuse_windows.go b/fuse_windows.go deleted file mode 100644 index c50a696..0000000 --- a/fuse_windows.go +++ /dev/null @@ -1,3 +0,0 @@ -package squashfs - -var ENODATA = windows.Errno(windows.ENODATA) diff --git a/go.mod b/go.mod deleted file mode 100644 index 8599653..0000000 --- a/go.mod +++ /dev/null @@ -1,14 +0,0 @@ -module github.com/CalebQ42/squashfs - -go 1.21 - -require ( - github.com/CalebQ42/fuse v0.1.0 - github.com/klauspost/compress v1.16.7 - github.com/pierrec/lz4/v4 v4.1.18 - github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e - github.com/seaweedfs/fuse v1.2.2 - github.com/therootcompany/xz v1.0.1 - github.com/ulikunitz/xz v0.5.11 - golang.org/x/sys v0.11.0 -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 6e8a10b..0000000 --- a/go.sum +++ /dev/null @@ -1,16 +0,0 @@ -github.com/CalebQ42/fuse v0.1.0 h1:KLCNjun7zcd2kBNVFfH+SWJyhuwJdE0nhw5/q8K8HGQ= -github.com/CalebQ42/fuse v0.1.0/go.mod h1:pJpoKG03HJKVhsp8o0YQYqmfbFsr3Eowt90yQGQVO+4= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= -github.com/pierrec/lz4/v4 v4.1.18/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/go.mod h1:9leZcVcItj6m9/CfHY5Em/iBrCz7js8LcRQGTKEEv2M= -github.com/seaweedfs/fuse v1.2.2 h1:01l8OjIdyATRNqVc/gDPgFobuC8ubQF3hRKOPColROw= -github.com/seaweedfs/fuse v1.2.2/go.mod h1:iwbDQv5BZACY54r6AO/6xsLNuMaYcBKSkLTZVfmK594= -github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= -github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= -github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= -github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/data/fullreader.go b/internal/data/fullreader.go deleted file mode 100644 index 2630357..0000000 --- a/internal/data/fullreader.go +++ /dev/null @@ -1,235 +0,0 @@ -package data - -import ( - "io" - - "github.com/CalebQ42/squashfs/internal/decompress" - "github.com/CalebQ42/squashfs/internal/toreader" -) - -type FullReader struct { - r io.ReaderAt - d decompress.Decompressor - fragRdr func() (io.Reader, error) - sizes []uint32 - blockSize uint32 - start uint64 - fileSize uint64 -} - -func NewFullReader(r io.ReaderAt, start uint64, d decompress.Decompressor, blockSizes []uint32, blockSize uint32, fileSize uint64) *FullReader { - return &FullReader{ - r: r, - start: start, - blockSize: blockSize, - sizes: blockSizes, - d: d, - fileSize: fileSize, - } -} - -func (r *FullReader) AddFragment(rdr func() (io.Reader, error)) { - r.fragRdr = rdr - r.sizes = append(r.sizes, 0) -} - -type outDat struct { - err error - data []byte - i int -} - -func (r FullReader) process(index int, offset int64, out chan outDat) { - var err error - var dat []byte - var rdr io.ReadCloser - size := realSize(r.sizes[index]) - if size == 0 { - outSize := r.blockSize - if r.fileSize < uint64(r.blockSize) { - outSize = uint32(r.fileSize) - } - out <- outDat{ - i: index, - err: nil, - data: make([]byte, outSize), - } - return - } - // rdr := io.LimitReader(toreader.NewReader(r.r, offset), int64(size)) - if size == r.sizes[index] { - if dec, ok := r.d.(decompress.Decoder); ok { - dat = make([]byte, size) - _, err = r.r.ReadAt(dat, offset) - if err == nil { - dat, err = dec.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) - } - } - } else { - dat = make([]byte, size) - _, err = r.r.ReadAt(dat, offset) - } - out <- outDat{ - i: index, - err: err, - data: dat, - } - if clr, ok := rdr.(io.Closer); ok { - clr.Close() - } -} - -func (r FullReader) ReadAt(p []byte, off int64) (n int, err error) { - out := make(chan outDat, len(r.sizes)) - offset := r.start - 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++ { - if i < int(start) || i > end { - offset += uint64(realSize(r.sizes[i])) - continue - } - if i == num-1 && r.fragRdr != nil { - go func() { - rdr, e := r.fragRdr() - if e != nil { - out <- outDat{ - i: num - 1, - err: e, - } - return - } - dat, e := io.ReadAll(rdr) - out <- outDat{ - i: num - 1, - err: e, - data: dat, - } - if clr, ok := rdr.(io.Closer); ok { - clr.Close() - } - }() - continue - } - go r.process(i, int64(offset), out) - offset += uint64(realSize(r.sizes[i])) - } - cache := make(map[int]outDat) - for cur := start; cur < int64(end); { - dat := <-out - if dat.err != nil { - err = dat.err - return - } - if dat.i != int(cur) { - cache[dat.i] = dat - continue - } - if cur == start { - dat.data = dat.data[off%int64(r.blockSize):] - } - for i := range dat.data { - p[n+i] = dat.data[i] - } - n += len(dat.data) - cur++ - var ok bool - for { - dat, ok = cache[int(cur)] - if !ok { - break - } - for i := range dat.data { - p[n+i] = dat.data[i] - } - n += len(dat.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) { - out := make(chan outDat, len(r.sizes)) - offset := r.start - num := len(r.sizes) - for i := 0; i < num; i++ { - if i == num-1 && r.fragRdr != nil { - go func() { - rdr, e := r.fragRdr() - if err != nil { - out <- outDat{ - i: num - 1, - err: e, - } - return - } - dat, e := io.ReadAll(rdr) - out <- outDat{ - i: num - 1, - err: e, - data: dat, - } - if clr, ok := rdr.(io.Closer); ok { - clr.Close() - } - }() - continue - } - go r.process(i, int64(offset), out) - offset += uint64(realSize(r.sizes[i])) - } - cache := make(map[int]outDat) - var tmpN int - for cur := 0; cur < num; { - dat := <-out - if dat.err != nil { - err = dat.err - return - } - if dat.i != cur { - cache[dat.i] = dat - continue - } - tmpN, err = w.Write(dat.data) - n += int64(tmpN) - if err != nil { - return - } - cur++ - var ok bool - for { - dat, ok = cache[cur] - if !ok { - break - } - tmpN, err = w.Write(dat.data) - n += int64(tmpN) - if err != nil { - return - } - cur++ - } - } - return -} diff --git a/internal/data/reader.go b/internal/data/reader.go deleted file mode 100644 index a1ddd05..0000000 --- a/internal/data/reader.go +++ /dev/null @@ -1,104 +0,0 @@ -package data - -import ( - "bytes" - "io" - - "github.com/CalebQ42/squashfs/internal/decompress" -) - -type Reader struct { - master io.Reader - cur io.Reader - fragRdr io.Reader - d decompress.Decompressor - comRdr io.Reader - blockSizes []uint32 - blockSize uint32 - resetable bool - fileSize uint64 -} - -func NewReader(r io.Reader, d decompress.Decompressor, blockSizes []uint32, blockSize uint32, fileSize uint64) *Reader { - return &Reader{ - d: d, - master: r, - blockSizes: blockSizes, - blockSize: blockSize, - resetable: true, - fileSize: fileSize, - } -} - -func (r *Reader) AddFragment(rdr io.Reader) { - r.fragRdr = rdr - r.blockSizes = append(r.blockSizes, 0) -} - -func realSize(siz uint32) uint32 { - return siz &^ (1 << 24) -} - -func (r *Reader) advance() (err error) { - if clr, ok := r.cur.(io.Closer); ok { - clr.Close() - } - if len(r.blockSizes) == 0 { - return io.EOF - } - if len(r.blockSizes) == 1 && r.fragRdr != nil { - r.cur = r.fragRdr - } else { - size := realSize(r.blockSizes[0]) - if size == 0 { - outSize := r.blockSize - if r.fileSize < uint64(r.blockSize) { - outSize = uint32(r.fileSize) - } - r.cur = bytes.NewReader(make([]byte, outSize)) - } else { - r.cur = io.LimitReader(r.master, int64(size)) - if size == r.blockSizes[0] { - if rs, ok := r.d.(decompress.Resetable); ok { - if r.comRdr == nil { - r.cur, err = r.d.Reader(r.cur) - if err != nil { - return - } - } else { - err = rs.Reset(r.comRdr, r.cur) - r.cur = r.comRdr - } - } else { - r.cur, err = r.d.Reader(r.cur) - } - } - } - } - r.blockSizes = r.blockSizes[1:] - return -} - -func (r *Reader) Read(p []byte) (n int, err error) { - if r.cur == nil { - err = r.advance() - if err != nil { - return - } - } - n, err = r.cur.Read(p) - if err == io.EOF { - err = r.advance() - if err != nil { - return - } - var tmpN int - tmp := make([]byte, len(p)-n) - tmpN, err = r.Read(tmp) - for i := range tmp { - p[n+i] = tmp[i] - } - n += tmpN - } - return -} diff --git a/internal/decompress/gzip.go b/internal/decompress/gzip.go deleted file mode 100644 index 26feccd..0000000 --- a/internal/decompress/gzip.go +++ /dev/null @@ -1,17 +0,0 @@ -package decompress - -import ( - "io" - - "github.com/klauspost/compress/zlib" -) - -type GZip struct{} - -func (g GZip) Reader(src io.Reader) (io.ReadCloser, error) { - return zlib.NewReader(src) -} - -func (g GZip) Reset(old, src io.Reader) error { - return old.(zlib.Resetter).Reset(src, nil) -} diff --git a/internal/decompress/interface.go b/internal/decompress/interface.go deleted file mode 100644 index f6ac915..0000000 --- a/internal/decompress/interface.go +++ /dev/null @@ -1,22 +0,0 @@ -package decompress - -import ( - "io" -) - -type Decompressor interface { - //Creates a new decompressor reading from src. - Reader(src io.Reader) (io.ReadCloser, error) -} - -type Resetable interface { - //Reset attempts to re-use an old decompressor with new data. - //Will return ErrNotResetable if not Resetable(). - //Must ALWAYS be provided with a reader created with Reader. - Reset(old, src io.Reader) error -} - -type Decoder interface { - //Decodes a chunk of data all at once. - Decode(in []byte) ([]byte, error) -} diff --git a/internal/decompress/lz4.go b/internal/decompress/lz4.go deleted file mode 100644 index 2a53ac7..0000000 --- a/internal/decompress/lz4.go +++ /dev/null @@ -1,18 +0,0 @@ -package decompress - -import ( - "io" - - "github.com/pierrec/lz4/v4" -) - -type Lz4 struct{} - -func (l Lz4) Reader(r io.Reader) (io.ReadCloser, error) { - return io.NopCloser(lz4.NewReader(r)), nil -} - -func (l Lz4) Reset(old, src io.Reader) error { - old.(*lz4.Reader).Reset(src) - return nil -} diff --git a/internal/decompress/lzma.go b/internal/decompress/lzma.go deleted file mode 100644 index 9add4ae..0000000 --- a/internal/decompress/lzma.go +++ /dev/null @@ -1,14 +0,0 @@ -package decompress - -import ( - "io" - - "github.com/ulikunitz/xz/lzma" -) - -type Lzma struct{} - -func (l Lzma) Reader(r io.Reader) (io.ReadCloser, error) { - rdr, err := lzma.NewReader(r) - return io.NopCloser(rdr), err -} diff --git a/internal/decompress/lzo.go b/internal/decompress/lzo.go deleted file mode 100644 index 76333e7..0000000 --- a/internal/decompress/lzo.go +++ /dev/null @@ -1,18 +0,0 @@ -package decompress - -import ( - "bytes" - "io" - - "github.com/rasky/go-lzo" -) - -type Lzo struct{} - -func (l Lzo) Reader(r io.Reader) (io.ReadCloser, error) { - cache, err := lzo.Decompress1X(r, 0, 0) - if err != nil { - return nil, err - } - return io.NopCloser(bytes.NewReader(cache)), nil -} diff --git a/internal/decompress/xz.go b/internal/decompress/xz.go deleted file mode 100644 index 8bc2c80..0000000 --- a/internal/decompress/xz.go +++ /dev/null @@ -1,18 +0,0 @@ -package decompress - -import ( - "io" - - "github.com/therootcompany/xz" -) - -type Xz struct{} - -func (x Xz) Reader(r io.Reader) (io.ReadCloser, error) { - rdr, err := xz.NewReader(r, 0) - return io.NopCloser(rdr), err -} - -func (x Xz) Reset(old, src io.Reader) error { - return old.(*xz.Reader).Reset(src) -} diff --git a/internal/decompress/zstd.go b/internal/decompress/zstd.go deleted file mode 100644 index dde62ff..0000000 --- a/internal/decompress/zstd.go +++ /dev/null @@ -1,27 +0,0 @@ -package decompress - -import ( - "io" - - "github.com/klauspost/compress/zstd" -) - -type Zstd struct { - writeToReader *zstd.Decoder -} - -func (z Zstd) Reader(src io.Reader) (io.ReadCloser, error) { - r, err := zstd.NewReader(src) - return r.IOReadCloser(), err -} - -func (z Zstd) Reset(old, src io.Reader) error { - return old.(*zstd.Decoder).Reset(src) -} - -func (z Zstd) Decode(in []byte) ([]byte, error) { - if z.writeToReader == nil { - z.writeToReader, _ = zstd.NewReader(nil) - } - return z.writeToReader.DecodeAll(in, nil) -} diff --git a/internal/directory/directory.go b/internal/directory/directory.go deleted file mode 100644 index 7f49bc3..0000000 --- a/internal/directory/directory.go +++ /dev/null @@ -1,80 +0,0 @@ -package directory - -import ( - "bytes" - "encoding/binary" - "io" -) - -type header struct { - Entries uint32 - InodeStart uint32 - Num uint32 -} - -type entryInit struct { - Offset uint16 - NumOffset int16 - Type uint16 - NameSize uint16 -} - -type entry struct { - entryInit - Name []byte -} - -type Entry struct { - Name string - BlockStart uint32 - Type uint16 - Offset uint16 - Num uint32 -} - -func readEntry(r io.Reader) (e entry, err error) { - err = binary.Read(r, binary.LittleEndian, &e.entryInit) - if err != nil { - return - } - e.Name = make([]byte, e.NameSize+1) - err = binary.Read(r, binary.LittleEndian, &e.Name) - return -} - -func ReadEntries(rdr io.Reader, size uint32) (e []Entry, err error) { - dat := make([]byte, size-3) - rdr.Read(dat) - r := bytes.NewReader(dat) - var h header - var en entry - for { - err = binary.Read(r, binary.LittleEndian, &h) - if err == io.EOF { - err = nil - return - } else if err != nil { - return - } - h.Entries++ - for i := 0; i < int(h.Entries); i++ { - if i != 0 && i%256 == 0 { - err = binary.Read(r, binary.LittleEndian, &h) - if err != nil { - return - } - } - en, err = readEntry(r) - if err != nil { - return - } - e = append(e, Entry{ - Name: string(en.Name), - BlockStart: h.InodeStart, - Type: en.Type, - Offset: en.Offset, - Num: h.Num + uint32(en.NumOffset), - }) - } - } -} diff --git a/internal/inode/dir.go b/internal/inode/dir.go deleted file mode 100644 index bffe973..0000000 --- a/internal/inode/dir.go +++ /dev/null @@ -1,65 +0,0 @@ -package inode - -import ( - "encoding/binary" - "io" -) - -type Directory struct { - BlockStart uint32 - LinkCount uint32 - Size uint16 - Offset uint16 - ParentNum uint32 -} - -type eDirectoryInit struct { - LinkCount uint32 - Size uint32 - BlockStart uint32 - ParentNum uint32 - IndCount uint16 - Offset uint16 - XattrInd uint32 -} - -type EDirectory struct { - eDirectoryInit - Indexes []DirectoryIndex -} - -type directoryIndexInit struct { - Ind uint32 - Start uint32 - NameSize uint32 -} - -type DirectoryIndex struct { - directoryIndexInit - Name []byte -} - -func ReadDir(r io.Reader) (d Directory, err error) { - err = binary.Read(r, binary.LittleEndian, &d) - return -} - -func ReadEDir(r io.Reader) (d EDirectory, err error) { - err = binary.Read(r, binary.LittleEndian, &d.eDirectoryInit) - if err != nil { - return - } - d.Indexes = make([]DirectoryIndex, d.IndCount) - for i := range d.Indexes { - err = binary.Read(r, binary.LittleEndian, &d.Indexes[i].directoryIndexInit) - if err != nil { - return - } - d.Indexes[i].Name = make([]byte, d.Indexes[i].NameSize+1) - err = binary.Read(r, binary.LittleEndian, &d.Indexes[i].Name) - if err != nil { - return - } - } - return -} diff --git a/internal/inode/file.go b/internal/inode/file.go deleted file mode 100644 index 1b4d461..0000000 --- a/internal/inode/file.go +++ /dev/null @@ -1,62 +0,0 @@ -package inode - -import ( - "encoding/binary" - "io" - "math" -) - -type fileInit struct { - BlockStart uint32 - FragInd uint32 - FragOffset uint32 - Size uint32 -} - -type File struct { - fileInit - BlockSizes []uint32 -} - -type eFileInit struct { - BlockStart uint64 - Size uint64 - Sparse uint64 - LinkCount uint32 - FragInd uint32 - FragOffset uint32 - XattrInd uint32 -} - -type EFile struct { - eFileInit - BlockSizes []uint32 -} - -func ReadFile(r io.Reader, blockSize uint32) (f File, err error) { - err = binary.Read(r, binary.LittleEndian, &f.fileInit) - if err != nil { - return - } - toRead := int(math.Floor(float64(f.Size) / float64(blockSize))) - if f.FragInd == 0xFFFFFFFF && f.Size%blockSize > 0 { - toRead++ - } - f.BlockSizes = make([]uint32, toRead) - err = binary.Read(r, binary.LittleEndian, &f.BlockSizes) - return -} - -func ReadEFile(r io.Reader, blockSize uint32) (f EFile, err error) { - err = binary.Read(r, binary.LittleEndian, &f.eFileInit) - if err != nil { - return - } - toRead := int(math.Floor(float64(f.Size) / float64(blockSize))) - if f.FragInd == 0xFFFFFFFF && f.Size%uint64(blockSize) > 0 { - toRead++ - } - f.BlockSizes = make([]uint32, toRead) - err = binary.Read(r, binary.LittleEndian, &f.BlockSizes) - return -} diff --git a/internal/inode/inode.go b/internal/inode/inode.go deleted file mode 100644 index 11976f5..0000000 --- a/internal/inode/inode.go +++ /dev/null @@ -1,143 +0,0 @@ -package inode - -import ( - "encoding/binary" - "errors" - "io" - "io/fs" - "strconv" -) - -const ( - Dir = uint16(iota + 1) - Fil - Sym - Block - Char - Fifo - Sock - EDir - EFil - ESym - EBlock - EChar - EFifo - ESock -) - -type Header struct { - Type uint16 - Perm uint16 - UidInd uint16 - GidInd uint16 - ModTime uint32 - Num uint32 -} - -type Inode struct { - Header - Data any -} - -func Read(r io.Reader, blockSize uint32) (i Inode, err error) { - err = binary.Read(r, binary.LittleEndian, &i.Header) - if err != nil { - return - } - switch i.Type { - case Dir: - i.Data, err = ReadDir(r) - case Fil: - i.Data, err = ReadFile(r, blockSize) - case Sym: - i.Data, err = ReadSym(r) - case Block: - fallthrough - case Char: - i.Data, err = ReadDevice(r) - case Fifo: - fallthrough - case Sock: - i.Data, err = ReadIPC(r) - case EDir: - i.Data, err = ReadEDir(r) - case EFil: - i.Data, err = ReadEFile(r, blockSize) - case ESym: - i.Data, err = ReadESym(r) - case EBlock: - fallthrough - case EChar: - i.Data, err = ReadEDevice(r) - case EFifo: - fallthrough - case ESock: - i.Data, err = ReadEIPC(r) - default: - return i, errors.New("invalid inode type " + strconv.Itoa(int(i.Type))) - } - 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 - } -} diff --git a/internal/inode/misc.go b/internal/inode/misc.go deleted file mode 100644 index 8ba0b61..0000000 --- a/internal/inode/misc.go +++ /dev/null @@ -1,45 +0,0 @@ -package inode - -import ( - "encoding/binary" - "io" -) - -type Device struct { - LinkCount uint32 - Dev uint32 -} - -type EDevice struct { - Device - XattrInd uint32 -} - -func ReadDevice(r io.Reader) (d Device, err error) { - err = binary.Read(r, binary.LittleEndian, &d) - return -} - -func ReadEDevice(r io.Reader) (d EDevice, err error) { - err = binary.Read(r, binary.LittleEndian, &d) - return -} - -type IPC struct { - LinkCount uint32 -} - -type EIPC struct { - IPC - XattrInd uint32 -} - -func ReadIPC(r io.Reader) (i IPC, err error) { - err = binary.Read(r, binary.LittleEndian, &i) - return -} - -func ReadEIPC(r io.Reader) (i EIPC, err error) { - err = binary.Read(r, binary.LittleEndian, &i) - return -} diff --git a/internal/inode/sym.go b/internal/inode/sym.go deleted file mode 100644 index 43659bb..0000000 --- a/internal/inode/sym.go +++ /dev/null @@ -1,46 +0,0 @@ -package inode - -import ( - "encoding/binary" - "io" -) - -type symlinkInit struct { - LinkCount uint32 - TargetSize uint32 -} - -type Symlink struct { - symlinkInit - Target []byte -} - -type ESymlink struct { - symlinkInit - Target []byte - XattrInd uint32 -} - -func ReadSym(r io.Reader) (s Symlink, err error) { - err = binary.Read(r, binary.LittleEndian, &s.symlinkInit) - if err != nil { - return - } - s.Target = make([]byte, s.TargetSize) - err = binary.Read(r, binary.LittleEndian, &s.Target) - return -} - -func ReadESym(r io.Reader) (s ESymlink, err error) { - err = binary.Read(r, binary.LittleEndian, &s.symlinkInit) - if err != nil { - return - } - s.Target = make([]byte, s.TargetSize) - err = binary.Read(r, binary.LittleEndian, &s.Target) - if err != nil { - return - } - err = binary.Read(r, binary.LittleEndian, &s.XattrInd) - return -} diff --git a/internal/metadata/reader.go b/internal/metadata/reader.go deleted file mode 100644 index 3fd0499..0000000 --- a/internal/metadata/reader.go +++ /dev/null @@ -1,81 +0,0 @@ -package metadata - -import ( - "encoding/binary" - "io" - - "github.com/CalebQ42/squashfs/internal/decompress" -) - -type Reader struct { - master io.Reader - cur io.Reader - d decompress.Decompressor - comRdr io.Reader -} - -func NewReader(master io.Reader, d decompress.Decompressor) *Reader { - return &Reader{ - master: master, - d: d, - } -} - -func realSize(siz uint16) uint16 { - return siz &^ 0x8000 -} - -func (r *Reader) advance() (err error) { - if _, ok := r.d.(decompress.Resetable); !ok { - if clr, ok := r.cur.(io.Closer); ok { - clr.Close() - } - } - var raw uint16 - err = binary.Read(r.master, binary.LittleEndian, &raw) - if err != nil { - return - } - size := realSize(raw) - r.cur = io.LimitReader(r.master, int64(size)) - if size == raw { - if rs, ok := r.d.(decompress.Resetable); ok { - if r.comRdr == nil { - r.cur, err = r.d.Reader(r.cur) - if err != nil { - return - } - } else { - err = rs.Reset(r.comRdr, r.cur) - r.cur = r.comRdr - } - } else { - r.cur, err = r.d.Reader(r.cur) - } - } - return -} - -func (r *Reader) Read(p []byte) (n int, err error) { - if r.cur == nil { - err = r.advance() - if err != nil { - return - } - } - n, err = r.cur.Read(p) - if err == io.EOF { - err = r.advance() - if err != nil { - return - } - var tmpN int - tmp := make([]byte, len(p)-n) - tmpN, err = r.Read(tmp) - for i := 0; i < tmpN; i++ { - p[n+i] = tmp[i] - } - n += tmpN - } - return -} diff --git a/internal/threadmanager/manager.go b/internal/threadmanager/manager.go deleted file mode 100644 index 6ee48be..0000000 --- a/internal/threadmanager/manager.go +++ /dev/null @@ -1,23 +0,0 @@ -package threadmanager - -type Manager struct { - c chan int -} - -func NewManager(maxRoutines int) *Manager { - m := &Manager{ - c: make(chan int, maxRoutines), - } - for i := 0; i < maxRoutines; i++ { - m.c <- i - } - return m -} - -func (m *Manager) Lock() int { - return <-m.c -} - -func (m *Manager) Unlock(n int) { - m.c <- n -} diff --git a/internal/toreader/offsetreader.go b/internal/toreader/offsetreader.go deleted file mode 100644 index 0192177..0000000 --- a/internal/toreader/offsetreader.go +++ /dev/null @@ -1,19 +0,0 @@ -package toreader - -import "io" - -type OffsetReader struct { - r io.ReaderAt - off int64 -} - -func NewOffsetReader(r io.ReaderAt, off int64) *OffsetReader { - return &OffsetReader{ - r: r, - off: off, - } -} - -func (r OffsetReader) ReadAt(p []byte, off int64) (n int, e error) { - return r.r.ReadAt(p, off+r.off) -} diff --git a/internal/toreader/reader.go b/internal/toreader/reader.go deleted file mode 100644 index 6f711fd..0000000 --- a/internal/toreader/reader.go +++ /dev/null @@ -1,25 +0,0 @@ -package toreader - -import "io" - -type Reader struct { - r io.ReaderAt - off int64 -} - -func NewReader(r io.ReaderAt, start int64) *Reader { - return &Reader{ - r: r, - off: start, - } -} - -func (r *Reader) Read(p []byte) (n int, err error) { - n, err = r.r.ReadAt(p, r.off) - r.off += int64(n) - return -} - -func (r Reader) Offset() int64 { - return r.off -} diff --git a/internal/toreader/readerat.go b/internal/toreader/readerat.go deleted file mode 100644 index b556ca7..0000000 --- a/internal/toreader/readerat.go +++ /dev/null @@ -1,24 +0,0 @@ -package toreader - -import "io" - -type ReaderAt struct { - d []byte -} - -func NewReaderAt(r io.Reader) (ra *ReaderAt, err error) { - ra = new(ReaderAt) - ra.d, err = io.ReadAll(r) - return -} - -func (r ReaderAt) ReadAt(p []byte, off int64) (n int, err error) { - if int(off) >= len(r.d) { - return 0, io.EOF - } - n = copy(p, r.d[off:]) - if n != len(p) { - err = io.EOF - } - return -} diff --git a/reader.go b/reader.go deleted file mode 100644 index 746cd02..0000000 --- a/reader.go +++ /dev/null @@ -1,234 +0,0 @@ -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" - fuse2 "github.com/seaweedfs/fuse" -) - -type Reader struct { - *FS - con *fuse.Conn - con2 *fuse2.Conn - mountDone chan struct{} - mount2Done 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) -} diff --git a/reader_file.go b/reader_file.go deleted file mode 100644 index 73adac6..0000000 --- a/reader_file.go +++ /dev/null @@ -1,518 +0,0 @@ -package squashfs - -import ( - "errors" - "io" - "io/fs" - "log" - "os" - "os/exec" - "path/filepath" - "runtime" - "strconv" - "strings" - - "github.com/CalebQ42/squashfs/internal/data" - "github.com/CalebQ42/squashfs/internal/directory" - "github.com/CalebQ42/squashfs/internal/inode" - "github.com/CalebQ42/squashfs/internal/threadmanager" -) - -// File represents a file inside a squashfs archive. -type File struct { - i inode.Inode - rdr io.Reader - fullRdr *data.FullReader - r *Reader - parent *FS - e directory.Entry - dirsRead int -} - -var ( - ErrReadNotFile = errors.New("read called on non-file") -) - -func (r Reader) newFile(en directory.Entry, parent *FS) (*File, error) { - i, err := r.inodeFromDir(en) - if err != nil { - return nil, err - } - var rdr io.Reader - var full *data.FullReader - if i.Type == inode.Fil || i.Type == inode.EFil { - full, rdr, err = r.getReaders(i) - if err != nil { - return nil, err - } - } - return &File{ - e: en, - i: i, - rdr: rdr, - fullRdr: full, - r: &r, - parent: parent, - }, nil -} - -// Stat returns the File's fs.FileInfo -func (f File) Stat() (fs.FileInfo, error) { - return newFileInfo(f.e, f.i), nil -} - -// Mode returns the file's fs.FileMode -func (f File) Mode() fs.FileMode { - switch f.e.Type { - case inode.Dir: - return fs.FileMode(f.i.Perm) | fs.ModeDir - case inode.Char: - return fs.FileMode(f.i.Perm) | fs.ModeCharDevice - case inode.Block: - return fs.FileMode(f.i.Perm) | fs.ModeDevice - case inode.Sym: - return fs.FileMode(f.i.Perm) | fs.ModeSymlink - } - return fs.FileMode(f.i.Perm) -} - -// Read reads the data from the file. Only works if file is a normal file. -func (f File) Read(p []byte) (int, error) { - if f.i.Type != inode.Fil && f.i.Type != inode.EFil { - return 0, ErrReadNotFile - } - if f.rdr == nil { - return 0, fs.ErrClosed - } - return f.rdr.Read(p) -} - -func (f File) ReadAt(p []byte, off int64) (int, error) { - if f.i.Type != inode.Fil && f.i.Type != inode.EFil { - return 0, ErrReadNotFile - } - 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) { - if f.i.Type != inode.Fil && f.i.Type != inode.EFil { - return 0, ErrReadNotFile - } - return f.fullRdr.WriteTo(w) -} - -// Close simply nils the underlying reader. -func (f *File) Close() error { - f.rdr = nil - return nil -} - -// 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. -func (f *File) ReadDir(n int) (out []fs.DirEntry, err error) { - if !f.IsDir() { - return nil, errors.New("file is not a directory") - } - ents, err := f.r.readDirectory(f.i) - if err != nil { - return nil, err - } - start, end := 0, len(ents) - if n > 0 { - start, end = f.dirsRead, f.dirsRead+n - if end > len(f.r.e) { - end = len(f.r.e) - err = io.EOF - } - } - var fi fileInfo - for _, e := range ents[start:end] { - fi, err = f.r.newFileInfo(e) - if err != nil { - f.dirsRead += len(out) - return - } - out = append(out, fs.FileInfoToDirEntry(fi)) - } - f.dirsRead += len(out) - return -} - -// FS returns the File as a FS. -func (f *File) FS() (*FS, error) { - if !f.IsDir() { - return nil, errors.New("File is not a directory") - } - ents, err := f.r.readDirectory(f.i) - if err != nil { - return nil, err - } - return &FS{ - File: f, - e: ents, - }, nil -} - -// IsDir Yep. -func (f File) IsDir() bool { - return f.i.Type == inode.Dir || f.i.Type == inode.EDir -} - -// IsRegular yep. -func (f File) IsRegular() bool { - return f.i.Type == inode.Fil || f.i.Type == inode.EFil -} - -// IsSymlink yep. -func (f File) IsSymlink() bool { - return f.i.Type == inode.Sym || f.i.Type == inode.ESym -} - -func (f File) isDeviceOrFifo() bool { - return f.i.Type == inode.Char || f.i.Type == inode.Block || f.i.Type == inode.EChar || f.i.Type == inode.EBlock || f.i.Type == inode.Fifo || f.i.Type == inode.EFifo -} - -func (f File) deviceDevices() (maj uint32, min uint32) { - var dev uint32 - if f.i.Type == inode.Char || f.i.Type == inode.Block { - dev = f.i.Data.(inode.Device).Dev - } else if f.i.Type == inode.EChar || f.i.Type == inode.EBlock { - dev = f.i.Data.(inode.EDevice).Dev - } - return dev >> 8, dev & 0x000FF -} - -// SymlinkPath returns the symlink's target path. Is the File isn't a symlink, returns an empty string. -func (f File) SymlinkPath() string { - switch f.i.Type { - case inode.Sym: - return string(f.i.Data.(inode.Symlink).Target) - case inode.ESym: - return string(f.i.Data.(inode.ESymlink).Target) - } - return "" -} - -func (f File) path() string { - if f.parent == nil { - return f.e.Name - } - return f.parent.path() + "/" + f.e.Name -} - -// 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 -func (f File) GetSymlinkFile() *File { - if !f.IsSymlink() { - return nil - } - if strings.HasPrefix(f.SymlinkPath(), "/") { - return nil - } - sym, err := f.parent.Open(f.SymlinkPath()) - if err != nil { - return nil - } - return sym.(*File) -} - -// ExtractionOptions are available options on how to extract. -type ExtractionOptions struct { - manager *threadmanager.Manager - LogOutput io.Writer //Where error log should write. - DereferenceSymlink bool //Replace symlinks with the target file. - UnbreakSymlink bool //Try to make sure symlinks remain unbroken when extracted, without changing the symlink. - Verbose bool //Prints extra info to log on an error. - IgnorePerm bool //Ignore file's permissions and instead use Perm. - Perm fs.FileMode //Permission to use when IgnorePerm. Defaults to 0755. - notFirst bool -} - -func DefaultOptions() *ExtractionOptions { - return &ExtractionOptions{ - Perm: 0755, - } -} - -// 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. -func (f File) ExtractTo(folder string) error { - return f.realExtract(folder, DefaultOptions()) -} - -// ExtractVerbose extracts the File to the folder with the Verbose option. -func (f File) ExtractVerbose(folder string) error { - op := DefaultOptions() - op.Verbose = true - return f.realExtract(folder, op) -} - -// ExtractIgnorePermissions extracts the File to the folder with the IgnorePerm option. -func (f File) ExtractIgnorePermissions(folder string) error { - op := DefaultOptions() - op.IgnorePerm = true - return f.realExtract(folder, op) -} - -// 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. -func (f File) ExtractSymlink(folder string) error { - op := DefaultOptions() - op.DereferenceSymlink = true - return f.realExtract(folder, op) -} - -// 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. -func (f File) ExtractWithOptions(folder string, op *ExtractionOptions) error { - if op.Verbose && op.LogOutput != nil { - log.SetOutput(op.LogOutput) - } - return f.realExtract(folder, op) -} - -func (f File) realExtract(folder string, op *ExtractionOptions) (err error) { - if op.manager == nil { - op.manager = threadmanager.NewManager(runtime.NumCPU()) - } - extDir := filepath.Join(folder, f.e.Name) - if !op.notFirst { - op.notFirst = true - if f.IsDir() { - extDir = folder - _, err = os.Open(folder) - if err != nil && os.IsNotExist(err) { - err = os.Mkdir(extDir, op.Perm) - } - if err != nil { - if op.Verbose { - log.Println("Error while making", folder) - } - return - } - if !op.IgnorePerm { - defer os.Chmod(extDir, f.Mode()) - defer os.Chown(extDir, int(f.r.ids[f.i.UidInd]), int(f.r.ids[f.i.GidInd])) - } - } - } - switch { - case f.IsDir(): - if folder != extDir && f.e.Name != "" { - //First extract it with a permisive permission. - err = os.Mkdir(extDir, op.Perm) - if err != nil { - if op.Verbose { - log.Println("Error while making directory", extDir) - } - return - } - //Then set it to it's actual permissions once we're done with it - if !op.IgnorePerm { - defer os.Chmod(extDir, f.Mode()) - defer os.Chown(extDir, int(f.r.ids[f.i.UidInd]), int(f.r.ids[f.i.GidInd])) - } - } - var filFS *FS - filFS, err = f.FS() - if err != nil { - if op.Verbose { - log.Println("Error while converting", f.path(), "to FS") - } - return err - } - errChan := make(chan error, len(filFS.e)) - files := make([]directory.Entry, 0) - //Focus on making the folder tree first... - var i int - for i = 0; i < len(filFS.e); i++ { - if filFS.e[i].Type == inode.Fil { - files = append(files, filFS.e[i]) - } else { - go func(index int) { - subF, goErr := f.r.newFile(filFS.e[index], filFS) - if goErr != nil { - if op.Verbose { - log.Println("Error while resolving", extDir) - } - errChan <- goErr - return - } - errChan <- subF.ExtractWithOptions(extDir, op) - }(i) - } - } - for i = 0; i < len(filFS.e)-len(files); i++ { - err = <-errChan - if err != nil { - return err - } - } - //Then we extract the files. - for i = 0; i < len(files); i++ { - go func(index int) { - n := op.manager.Lock() - defer op.manager.Unlock(n) - subF, goErr := f.r.newFile(files[index], filFS) - if goErr != nil { - if op.Verbose { - log.Println("Error while resolving", extDir) - } - errChan <- goErr - return - } - errChan <- subF.ExtractWithOptions(extDir, op) - }(i) - } - for i = 0; i < len(files); i++ { - err = <-errChan - if err != nil { - return err - } - } - case f.IsRegular(): - var fil *os.File - fil, err = os.Create(extDir) - if os.IsExist(err) { - os.Remove(extDir) - fil, err = os.Create(extDir) - if err != nil { - if op.Verbose { - log.Println("Error while creating", extDir) - } - return err - } - } else if err != nil { - if op.Verbose { - log.Println("Error while creating", extDir) - } - return err - } - defer fil.Close() - _, err = io.Copy(fil, f) - if err != nil { - if op.Verbose { - log.Println("Error while copying data to", extDir) - } - return err - } - if op.IgnorePerm { - os.Chmod(extDir, op.Perm|(f.Mode()&fs.ModeType)) - } else { - os.Chmod(extDir, f.Mode()) - os.Chown(extDir, int(f.r.ids[f.i.UidInd]), int(f.r.ids[f.i.GidInd])) - } - case f.IsSymlink(): - symPath := f.SymlinkPath() - if op.DereferenceSymlink { - fil := f.GetSymlinkFile() - if fil == nil { - if op.Verbose { - log.Println("Symlink path(", symPath, ") is unobtainable:", extDir) - } - return errors.New("cannot get symlink target") - } - fil.e.Name = f.e.Name - err = fil.realExtract(folder, op) - if err != nil { - if op.Verbose { - log.Println("Error while extracting the symlink's file:", extDir) - } - return err - } - return nil - } else if op.UnbreakSymlink { - fil := f.GetSymlinkFile() - if fil == nil { - if op.Verbose { - log.Println("Symlink path(", symPath, ") is unobtainable:", extDir) - } - return errors.New("cannot get symlink target") - } - extractLoc := filepath.Join(folder, filepath.Dir(symPath)) - err = fil.realExtract(extractLoc, op) - if err != nil { - if op.Verbose { - log.Println("Error while extracting ", extDir) - } - return err - } - } - err = os.Symlink(f.SymlinkPath(), extDir) - if os.IsExist(err) { - os.Remove(extDir) - err = os.Symlink(f.SymlinkPath(), extDir) - } - if err != nil { - if op.Verbose { - log.Println("Error while making symlink:", extDir) - } - return err - } - if op.IgnorePerm { - os.Chmod(extDir, op.Perm|(f.Mode()&fs.ModeType)) - } else { - os.Chmod(extDir, f.Mode()) - os.Chown(extDir, int(f.r.ids[f.i.UidInd]), int(f.r.ids[f.i.GidInd])) - } - case f.isDeviceOrFifo(): - if runtime.GOOS == "windows" { - if op.Verbose { - log.Println(extDir, "ignored since it's a device link and can't be created on Windows.") - } - return nil - } - _, err = exec.LookPath("mknod") - if err != nil { - if op.Verbose { - log.Println("Extracting Fifo IPC or Device and mknod is not in PATH") - } - return err - } - var typ string - if f.i.Type == inode.Char || f.i.Type == inode.EChar { - typ = "c" - } else if f.i.Type == inode.Block || f.i.Type == inode.EBlock { - typ = "b" - } else { //Fifo IPC - if runtime.GOOS == "darwin" { - if op.Verbose { - log.Println(extDir, "ignored since it's a Fifo file and can't be created on Darwin.") - } - return nil - } - typ = "p" - } - cmd := exec.Command("mknod", extDir, typ) - if typ != "p" { - maj, min := f.deviceDevices() - cmd.Args = append(cmd.Args, strconv.Itoa(int(maj)), strconv.Itoa(int(min))) - } - if op.Verbose { - cmd.Stdout = op.LogOutput - cmd.Stderr = op.LogOutput - } - err = cmd.Run() - if err != nil { - if op.Verbose { - log.Println("Error while running mknod for", extDir) - } - return err - } - if op.IgnorePerm { - os.Chmod(extDir, op.Perm|(f.Mode()&fs.ModeType)) - } else { - os.Chmod(extDir, f.Mode()) - os.Chown(extDir, int(f.r.ids[f.i.UidInd]), int(f.r.ids[f.i.GidInd])) - } - case f.e.Type == inode.Sock: - if op.Verbose { - log.Println(extDir, "ignored since it's a socket file.") - } - default: - return errors.New("Unsupported file type. Inode type: " + strconv.Itoa(int(f.i.Type))) - } - return nil -} diff --git a/reader_fileinfo.go b/reader_fileinfo.go deleted file mode 100644 index 4fc875c..0000000 --- a/reader_fileinfo.go +++ /dev/null @@ -1,66 +0,0 @@ -package squashfs - -import ( - "io/fs" - "time" - - "github.com/CalebQ42/squashfs/internal/directory" - "github.com/CalebQ42/squashfs/internal/inode" -) - -type fileInfo struct { - e directory.Entry - size int64 - perm uint32 - modTime uint32 -} - -func (r Reader) newFileInfo(e directory.Entry) (fileInfo, error) { - i, err := r.inodeFromDir(e) - if err != nil { - return fileInfo{}, err - } - return newFileInfo(e, i), nil -} - -func newFileInfo(e directory.Entry, i inode.Inode) fileInfo { - var size int64 - if i.Type == inode.Fil { - size = int64(i.Data.(inode.File).Size) - } else if i.Type == inode.EFil { - size = int64(i.Data.(inode.EFile).Size) - } - return fileInfo{ - e: e, - size: size, - perm: uint32(i.Perm), - modTime: i.ModTime, - } -} - -func (f fileInfo) Name() string { - return f.e.Name -} - -func (f fileInfo) Size() int64 { - return f.size -} - -func (f fileInfo) Mode() fs.FileMode { - if f.IsDir() { - return fs.FileMode(f.perm | uint32(fs.ModeDir)) - } - return fs.FileMode(f.perm) -} - -func (f fileInfo) ModTime() time.Time { - return time.Unix(int64(f.modTime), 0) -} - -func (f fileInfo) IsDir() bool { - return f.e.Type == inode.Dir -} - -func (f fileInfo) Sys() any { - return nil -} diff --git a/reader_frag.go b/reader_frag.go deleted file mode 100644 index 0e94e2e..0000000 --- a/reader_frag.go +++ /dev/null @@ -1,26 +0,0 @@ -package squashfs - -import ( - "bytes" - "io" - - "github.com/CalebQ42/squashfs/internal/toreader" -) - -type fragEntry struct { - Start uint64 - Size uint32 - _ uint32 -} - -func (r Reader) fragReader(index uint32, fragSize uint32) (io.Reader, error) { - realSize := r.fragEntries[index].Size &^ (1 << 24) - if realSize == 0 { - return bytes.NewReader(make([]byte, fragSize)), nil - } - rdr := io.LimitReader(toreader.NewReader(r.r, int64(r.fragEntries[index].Start)), int64(realSize)) - if realSize != r.fragEntries[index].Size { - return rdr, nil - } - return r.d.Reader(rdr) -} diff --git a/reader_fs.go b/reader_fs.go deleted file mode 100644 index 60296b7..0000000 --- a/reader_fs.go +++ /dev/null @@ -1,380 +0,0 @@ -package squashfs - -import ( - "bytes" - "io" - "io/fs" - "path" - "path/filepath" - "strings" - - "github.com/CalebQ42/squashfs/internal/directory" - "github.com/CalebQ42/squashfs/internal/inode" -) - -// FS is a fs.FS representation of a squashfs directory. -// Implements fs.GlobFS, fs.ReadDirFS, fs.ReadFileFS, fs.StatFS, and fs.SubFS -type FS struct { - *File - e []directory.Entry -} - -func (r Reader) newFS(e directory.Entry, parent *FS) (*FS, error) { - i, err := r.inodeFromDir(e) - if err != nil { - return nil, err - } - ents, err := r.readDirectory(i) - if err != nil { - return nil, err - } - return &FS{ - File: &File{ - i: i, - r: &r, - parent: parent, - e: e, - }, - e: ents, - }, nil -} - -// Opens the file at name. Returns a squashfs.File. -func (f FS) OpenFile(name string) (*File, error) { - name = filepath.Clean(name) - if !fs.ValidPath(name) { - return nil, &fs.PathError{ - Op: "open", - Path: name, - Err: fs.ErrInvalid, - } - } - if name == "." || name == "" { - return f.File, nil - } - split := strings.Split(name, "/") - for i := range f.e { - if f.e[i].Name != split[0] { - continue - } - if len(split) > 1 && f.e[i].Type != inode.Dir { - return nil, &fs.PathError{ - Op: "open", - Path: name, - Err: fs.ErrNotExist, - } - } - if len(split) > 1 { - newFS, err := f.r.newFS(f.e[i], &f) - if err != nil { - return nil, &fs.PathError{ - Op: "open", - Path: name, - Err: err, - } - } - out, err := newFS.OpenFile(strings.Join(split[1:], "/")) - if err != nil { - err.(*fs.PathError).Path = name - } - return out, err - } - out, err := f.r.newFile(f.e[i], &f) - if err != nil { - err = &fs.PathError{ - Op: "open", - Path: name, - Err: err, - } - } - return out, err - } - return nil, &fs.PathError{ - Op: "open", - Path: name, - Err: fs.ErrNotExist, - } -} - -// Opens the file at name. Returns a io/fs.File. -func (f FS) Open(name string) (fs.File, error) { - 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) { - pattern = filepath.Clean(pattern) - if !fs.ValidPath(pattern) { - return nil, &fs.PathError{ - Op: "glob", - Path: pattern, - Err: fs.ErrInvalid, - } - } - split := strings.Split(pattern, "/") - for i := 0; i < len(f.e); i++ { - if match, _ := path.Match(split[0], f.e[i].Name); match { - if len(split) == 1 { - out = append(out, f.e[i].Name) - continue - } - sub, err := f.Sub(split[0]) - if err != nil { - if pathErr, ok := err.(*fs.PathError); ok { - if pathErr.Err == fs.ErrNotExist { - continue - } - pathErr.Op = "glob" - pathErr.Path = pattern - return nil, pathErr - } - return nil, &fs.PathError{ - Op: "glob", - Path: pattern, - Err: err, - } - } - subGlob, err := sub.(fs.GlobFS).Glob(strings.Join(split[1:], "/")) - if err != nil { - if pathErr, ok := err.(*fs.PathError); ok { - if pathErr.Err == fs.ErrNotExist { - continue - } - pathErr.Op = "glob" - pathErr.Path = pattern - return nil, pathErr - } - return nil, &fs.PathError{ - Op: "glob", - Path: pattern, - Err: err, - } - } - for i := 0; i < len(subGlob); i++ { - subGlob[i] = f.File.e.Name + "/" + subGlob[i] - } - out = append(out, subGlob...) - } - } - return -} - -// ReadDir returns all the DirEntry returns all DirEntry's for the directory at name. -// If name is not a directory, returns an error. -func (f FS) ReadDir(name string) ([]fs.DirEntry, error) { - name = filepath.Clean(name) - if !fs.ValidPath(name) { - return nil, &fs.PathError{ - Op: "readdir", - Path: name, - Err: fs.ErrInvalid, - } - } - if name == "." || name == "" { - return f.File.ReadDir(-1) - } - split := strings.Split(name, "/") - for i := 0; i < len(f.e); i++ { - if split[0] == f.e[i].Name { - if len(split) == 1 { - fi, err := f.r.newFile(f.e[i], &f) - if err != nil { - return nil, &fs.PathError{ - Op: "readdir", - Path: name, - Err: err, - } - } - out, err := fi.ReadDir(-1) - if err != nil { - err = &fs.PathError{ - Op: "readdir", - Path: name, - Err: err, - } - } - return out, err - } - sub, err := f.Sub(split[0]) - if err != nil { - if pathErr, ok := err.(*fs.PathError); ok { - if pathErr.Err == fs.ErrNotExist { - continue - } - pathErr.Op = "readir" - pathErr.Path = name - return nil, pathErr - } - return nil, &fs.PathError{ - Op: "readdir", - Path: name, - Err: err, - } - } - redDir, err := sub.(fs.ReadDirFS).ReadDir(strings.Join(split[1:], "/")) - if err != nil { - if pathErr, ok := err.(*fs.PathError); ok { - if pathErr.Err == fs.ErrNotExist { - continue - } - pathErr.Op = "readdir" - pathErr.Path = name - return nil, pathErr - } - return nil, &fs.PathError{ - Op: "readdir", - Path: name, - Err: err, - } - } - return redDir, nil - } - } - return nil, &fs.PathError{ - Op: "readdir", - Path: name, - Err: fs.ErrNotExist, - } -} - -// ReadFile returns the data (in []byte) for the file at name. -func (f FS) ReadFile(name string) ([]byte, error) { - fil, err := f.Open(name) - if err != nil { - if pathErr, ok := err.(*fs.PathError); ok { - pathErr.Op = "readfile" - pathErr.Path = name - return nil, pathErr - } - } - var buf bytes.Buffer - _, err = io.Copy(&buf, fil) - if err != nil { - return nil, &fs.PathError{ - Op: "readfile", - Path: name, - Err: err, - } - } - return buf.Bytes(), nil -} - -// Stat returns the fs.FileInfo for the file at name. -func (f FS) Stat(name string) (fs.FileInfo, error) { - name = filepath.Clean(strings.TrimPrefix(name, "/")) - if !fs.ValidPath(name) { - return nil, &fs.PathError{ - Op: "stat", - Path: name, - Err: fs.ErrInvalid, - } - } - if name == "." || name == "" { - return f.File.Stat() - } - split := strings.Split(name, "/") - for i := 0; i < len(f.e); i++ { - if split[0] == f.e[i].Name { - if len(split) == 1 { - in, err := f.r.newFileInfo(f.e[i]) - if err != nil { - err = &fs.PathError{ - Op: "stat", - Path: name, - Err: err, - } - } - return in, err - } - sub, err := f.Sub(split[0]) - if err != nil { - if pathErr, ok := err.(*fs.PathError); ok { - if pathErr.Err == fs.ErrNotExist { - continue - } - pathErr.Op = "stat" - pathErr.Path = name - return nil, pathErr - } - return nil, &fs.PathError{ - Op: "stat", - Path: name, - Err: err, - } - } - stat, err := sub.(fs.StatFS).Stat(strings.Join(split[1:], "/")) - if err != nil { - if pathErr, ok := err.(*fs.PathError); ok { - if pathErr.Err == fs.ErrNotExist { - continue - } - pathErr.Op = "stat" - pathErr.Path = name - return nil, pathErr - } - return nil, &fs.PathError{ - Op: "stat", - Path: name, - Err: err, - } - } - return stat, nil - } - } - return nil, &fs.PathError{ - Op: "stat", - Path: name, - Err: fs.ErrNotExist, - } -} - -// Sub returns the FS at dir -func (f FS) Sub(dir string) (fs.FS, error) { - dir = filepath.Clean(dir) - if !fs.ValidPath(dir) { - return nil, &fs.PathError{ - Op: "sub", - Path: dir, - Err: fs.ErrInvalid, - } - } - if dir == "." || dir == "" { - return f, nil - } - split := strings.Split(dir, "/") - for i := range f.e { - if f.e[i].Name != split[0] { - continue - } - if f.e[i].Type != inode.Dir { - return nil, &fs.PathError{ - Op: "sub", - Path: dir, - Err: fs.ErrNotExist, - } - } - newFS, err := f.r.newFS(f.e[i], &f) - if err != nil { - return nil, &fs.PathError{ - Op: "sub", - Path: dir, - Err: err, - } - } - if len(split) > 1 { - ret, err := newFS.Sub(strings.Join(split[1:], "/")) - if err != nil { - err.(*fs.PathError).Path = dir - } - return ret, err - } - return newFS, nil - } - return nil, &fs.PathError{ - Op: "sub", - Path: dir, - Err: fs.ErrNotExist, - } -} diff --git a/reader_inode.go b/reader_inode.go deleted file mode 100644 index ec0516c..0000000 --- a/reader_inode.go +++ /dev/null @@ -1,117 +0,0 @@ -package squashfs - -import ( - "errors" - "io" - - "github.com/CalebQ42/squashfs/internal/data" - "github.com/CalebQ42/squashfs/internal/directory" - "github.com/CalebQ42/squashfs/internal/inode" - "github.com/CalebQ42/squashfs/internal/metadata" - "github.com/CalebQ42/squashfs/internal/toreader" -) - -func (r Reader) inodeFromRef(ref uint64) (i inode.Inode, err error) { - offset, meta := (ref>>16)+r.s.InodeTableStart, ref&0xFFFF - rdr := metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d) - _, err = rdr.Read(make([]byte, meta)) - if err != nil { - return - } - return inode.Read(rdr, r.s.BlockSize) -} - -func (r Reader) inodeFromDir(e directory.Entry) (i inode.Inode, err error) { - rdr := metadata.NewReader(toreader.NewReader(r.r, int64(uint64(e.BlockStart)+r.s.InodeTableStart)), r.d) - _, err = rdr.Read(make([]byte, e.Offset)) - if err != nil { - return - } - return inode.Read(rdr, r.s.BlockSize) -} - -func (r Reader) getReaders(i inode.Inode) (full *data.FullReader, rdr *data.Reader, err error) { - var fragOffset uint64 - var blockOffset uint64 - var blockSizes []uint32 - var fragInd uint32 - var fragSize uint32 - var fileSize uint64 - if i.Type == inode.Fil { - fragOffset = uint64(i.Data.(inode.File).FragOffset) - blockOffset = uint64(i.Data.(inode.File).BlockStart) - blockSizes = i.Data.(inode.File).BlockSizes - fragInd = i.Data.(inode.File).FragInd - fragSize = i.Data.(inode.File).Size % r.s.BlockSize - fileSize = uint64(i.Data.(inode.File).Size) - } else if i.Type == inode.EFil { - fragOffset = uint64(i.Data.(inode.EFile).FragOffset) - blockOffset = i.Data.(inode.EFile).BlockStart - blockSizes = i.Data.(inode.EFile).BlockSizes - fragInd = i.Data.(inode.EFile).FragInd - fragSize = uint32(i.Data.(inode.EFile).Size % uint64(r.s.BlockSize)) - fileSize = i.Data.(inode.EFile).Size - } else { - return nil, nil, errors.New("getReaders called on non-file type") - } - rdr = data.NewReader(toreader.NewReader(r.r, int64(blockOffset)), r.d, blockSizes, r.s.BlockSize, fileSize) - full = data.NewFullReader(r.r, uint64(blockOffset), r.d, blockSizes, r.s.BlockSize, fileSize) - if fragInd != 0xFFFFFFFF { - full.AddFragment(func() (io.Reader, error) { - var fragRdr io.Reader - fragRdr, err = r.fragReader(fragInd, fragSize) - if err != nil { - return nil, err - } - var n, tmpN int - for n != int(fragOffset) { - tmpN, err = fragRdr.Read(make([]byte, int(fragOffset)-n)) - if err != nil { - return nil, err - } - n += tmpN - } - fragRdr = io.LimitReader(fragRdr, int64(fragSize)) - return fragRdr, nil - }) - var fragRdr io.Reader - fragRdr, err = r.fragReader(fragInd, fragSize) - if err != nil { - return nil, nil, err - } - var n, tmpN int - for n != int(fragOffset) { - tmpN, err = fragRdr.Read(make([]byte, int(fragOffset)-n)) - if err != nil { - return nil, nil, err - } - n += tmpN - } - fragRdr = io.LimitReader(fragRdr, int64(fragSize)) - rdr.AddFragment(fragRdr) - } - return -} - -func (r Reader) readDirectory(i inode.Inode) ([]directory.Entry, error) { - var offset uint64 - var blockOffset uint16 - var size uint32 - if i.Type == inode.Dir { - offset = uint64(i.Data.(inode.Directory).BlockStart) - blockOffset = i.Data.(inode.Directory).Offset - size = uint32(i.Data.(inode.Directory).Size) - } else if i.Type == inode.EDir { - offset = uint64(i.Data.(inode.EDirectory).BlockStart) - blockOffset = i.Data.(inode.EDirectory).Offset - size = i.Data.(inode.EDirectory).Size - } else { - return nil, errors.New("readDirectory called on non-directory type") - } - rdr := metadata.NewReader(toreader.NewReader(r.r, int64(offset+r.s.DirTableStart)), r.d) - _, err := rdr.Read(make([]byte, blockOffset)) - if err != nil { - return nil, err - } - return directory.ReadEntries(rdr, size) -} diff --git a/squashfs_test.go b/squashfs_test.go deleted file mode 100644 index 4d42960..0000000 --- a/squashfs_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package squashfs_test - -//Actually proper tests go here. - -import ( - "errors" - "io" - "io/fs" - "net/http" - "os" - "os/exec" - "path/filepath" - "strconv" - "testing" - "time" - - "github.com/CalebQ42/squashfs" -) - -const ( - squashfsURL = "https://darkstorm.tech/files/LinuxPATest.sfs" - squashfsName = "bug.sqfs" - - filePath = "PortableApps/Notepad++Portable/App/DefaultData/Config/contextMenu.xml" -) - -func preTest(dir string) (fil *os.File, err error) { - fil, err = os.Open(filepath.Join(dir, squashfsName)) - if err != nil { - _, err = os.Open(dir) - if os.IsNotExist(err) { - err = os.Mkdir(dir, 0755) - } - if err != nil { - return - } - os.Remove(filepath.Join(dir, squashfsName)) - fil, err = os.Create(filepath.Join(dir, squashfsName)) - if err != nil { - return - } - var resp *http.Response - resp, err = http.DefaultClient.Get(squashfsURL) - if err != nil { - return - } - _, err = io.Copy(fil, resp.Body) - if err != nil { - return - } - } - _, err = exec.LookPath("unsquashfs") - if err != nil { - return - } - _, err = exec.LookPath("mksquashfs") - 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) { - tmpDir := "testing" - fil, err := preTest(tmpDir) - if err != nil { - b.Fatal(err) - } - libPath := filepath.Join(tmpDir, "ExtractLib") - unsquashPath := filepath.Join(tmpDir, "ExtractSquashfs") - os.RemoveAll(libPath) - os.RemoveAll(unsquashPath) - var libTime, unsquashTime time.Duration - start := time.Now() - rdr, err := squashfs.NewReader(fil) - if err != nil { - b.Fatal(err) - } - err = rdr.ExtractTo(libPath) - if err != nil { - b.Fatal(err) - } - libTime = time.Since(start) - cmd := exec.Command("unsquashfs", "-d", unsquashPath, fil.Name()) - start = time.Now() - err = cmd.Run() - if err != nil { - b.Fatal(err) - } - unsquashTime = time.Since(start) - b.Log("Library took:", libTime.Round(time.Millisecond)) - b.Log("unsquashfs took:", unsquashTime.Round(time.Millisecond)) - b.Log("unsquashfs is", strconv.FormatFloat(float64(libTime.Milliseconds())/float64(unsquashTime.Milliseconds()), 'f', 2, 64), "times faster") -} - -func TestExtractQuick(t *testing.T) { - //First, setup everything and extract the archive using the library and unsquashfs - - // tmpDir := b.TempDir() - tmpDir := "testing" - fil, err := preTest(tmpDir) - if err != nil { - t.Fatal(err) - } - libPath := filepath.Join(tmpDir, "ExtractLib") - unsquashPath := filepath.Join(tmpDir, "ExtractSquashfs") - os.RemoveAll(libPath) - os.RemoveAll(unsquashPath) - rdr, err := squashfs.NewReader(fil) - if err != nil { - t.Fatal(err) - } - os.RemoveAll(filepath.Join(tmpDir, "testLog.txt")) - logFil, _ := os.Create(filepath.Join(tmpDir, "testLog.txt")) - op := squashfs.DefaultOptions() - op.Verbose = true - op.IgnorePerm = true - op.LogOutput = logFil - err = rdr.ExtractWithOptions(libPath, op) - if err != nil { - t.Fatal(err) - } - cmd := exec.Command("unsquashfs", "-d", unsquashPath, fil.Name()) - err = cmd.Run() - if err != nil { - t.Fatal(err) - } - - //Then compare the sizes and existance between the two (using unsquashfs as a reference). - //If the file doesn't exist, or the size is different, we exit. - //TODO: Add long test that checks contents. - - squashFils := os.DirFS(unsquashPath) - err = fs.WalkDir(squashFils, ".", func(path string, d fs.DirEntry, _ error) error { - libFil, e := os.Open(filepath.Join(libPath, path)) - if e != nil { - return e - } - stat, _ := d.Info() - libStat, _ := libFil.Stat() - if stat.Size() != libStat.Size() { - t.Log(path, "not the same size between library and unsquashfs") - t.Log("File is", libStat.Size()) - t.Log("Should be", stat.Size()) - return errors.New("file not the correct size") - } - return nil - }) - if err != nil { - 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) - } - err = f.(*squashfs.File).ExtractWithOptions("testing", &squashfs.ExtractionOptions{Verbose: true}) - 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) - } - err = rdr.Mount("testing/fuseTest") - if err != nil { - t.Fatal(err) - } - defer rdr.Unmount() - rdr.MountWait() - t.Fatal("testing") -}