Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 89f28cec6e | |||
| c988309edc | |||
| e8a8c531a9 | |||
| 80ff4466ae | |||
| 64055a8a63 |
@@ -1,23 +1,28 @@
|
|||||||
package rawreader
|
package rawreader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ConvertReader(r io.Reader) RawReader {
|
func ConvertReader(r io.Reader) (RawReader, error) {
|
||||||
if rr, ok := r.(RawReader); ok {
|
if rr, ok := r.(RawReader); ok {
|
||||||
return rr
|
return rr, nil
|
||||||
}
|
}
|
||||||
if rs, is := r.(io.ReadSeeker); is {
|
if rs, is := r.(io.ReadSeeker); is {
|
||||||
return &fromReadSeeker{
|
return &fromReadSeeker{
|
||||||
ReadSeeker: rs,
|
ReadSeeker: rs,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err := io.Copy(buf, r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return &fromReader{
|
return &fromReader{
|
||||||
rdr: r,
|
data: buf.Bytes(),
|
||||||
cache: make([]byte, 0),
|
}, nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertReaderAt(r io.ReaderAt) RawReader {
|
func ConvertReaderAt(r io.ReaderAt) RawReader {
|
||||||
@@ -37,27 +42,21 @@ type RawReader interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type fromReader struct {
|
type fromReader struct {
|
||||||
rdr io.Reader
|
data []byte
|
||||||
cache []byte
|
|
||||||
off int
|
off int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *fromReader) increaseCache(len int) error {
|
|
||||||
newCache := make([]byte, len)
|
|
||||||
_, err := r.rdr.Read(newCache)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r.cache = append(r.cache, newCache...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *fromReader) ReadAt(p []byte, off int64) (n int, err error) {
|
func (r *fromReader) ReadAt(p []byte, off int64) (n int, err error) {
|
||||||
if int(off)+len(p) > len(r.cache) {
|
n = len(p)
|
||||||
r.increaseCache((int(off) + len(p)) - len(r.cache))
|
if int(off)+len(p) > len(r.data) {
|
||||||
|
n = len(r.data) - int(off)
|
||||||
|
err = io.EOF
|
||||||
}
|
}
|
||||||
for i := int64(0); i < int64(len(p)); i++ {
|
if n < 0 {
|
||||||
p[i] = r.cache[off+i]
|
n = 0
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
p[i] = r.data[int(off)+i]
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -65,32 +64,35 @@ func (r *fromReader) ReadAt(p []byte, off int64) (n int, err error) {
|
|||||||
func (r *fromReader) Seek(off int64, whence int) (n int64, err error) {
|
func (r *fromReader) Seek(off int64, whence int) (n int64, err error) {
|
||||||
switch whence {
|
switch whence {
|
||||||
case io.SeekEnd:
|
case io.SeekEnd:
|
||||||
return 0, errors.New("cannot SeekEnd RawReader")
|
r.off = len(r.data) - int(off)
|
||||||
|
if r.off < 0 {
|
||||||
|
r.off = 0
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
case io.SeekCurrent:
|
case io.SeekCurrent:
|
||||||
r.off += int(off)
|
r.off += int(off)
|
||||||
case io.SeekStart:
|
case io.SeekStart:
|
||||||
r.off = int(off)
|
r.off = int(off)
|
||||||
}
|
}
|
||||||
if r.off > len(r.cache) {
|
if r.off > len(r.data) {
|
||||||
err = r.increaseCache(len(r.cache) - r.off)
|
r.off = len(r.data)
|
||||||
if err != nil {
|
return int64(r.off), io.EOF
|
||||||
r.off = len(r.cache)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return int64(r.off), err
|
return int64(r.off), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *fromReader) Read(p []byte) (n int, err error) {
|
func (r *fromReader) Read(p []byte) (n int, err error) {
|
||||||
if len(p)+r.off > len(r.cache) {
|
n = len(p)
|
||||||
err = r.increaseCache((len(p) + r.off) - len(r.cache))
|
if r.off+len(p) > len(r.data) {
|
||||||
if err != nil {
|
n = len(r.data) - r.off
|
||||||
return
|
err = io.EOF
|
||||||
}
|
}
|
||||||
|
if n < 0 {
|
||||||
|
n = 0
|
||||||
}
|
}
|
||||||
for i := 0; i < len(p); i++ {
|
for i := 0; i < n; i++ {
|
||||||
p[i] = r.cache[r.off+i]
|
p[i] = r.data[r.off+i]
|
||||||
}
|
}
|
||||||
r.off += len(p)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,12 +46,18 @@ func NewSquashfsReader(r io.ReaderAt) (*Reader, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//NewSquashfsReaderFromReader returns a new squashfs.Reader from an io.Reader.
|
//NewSquashfsReaderFromReader returns a new squashfs.Reader from an io.Reader.
|
||||||
//If possible, try to use a io.ReaderAt.
|
//If the io.Reader implements io.Seeker, the seek functions are used.
|
||||||
//With an io.Reader, much of the data has to be cached to memory.
|
//It is NOT recommended to use a pure io.Reader as due to how squashfs
|
||||||
|
//archives are formatted, the ENTIRETY of the io.Reader's data is loaded into
|
||||||
|
//memory first before it can be used.
|
||||||
func NewSquashfsReaderFromReader(r io.Reader) (*Reader, error) {
|
func NewSquashfsReaderFromReader(r io.Reader) (*Reader, error) {
|
||||||
var rdr Reader
|
var rdr Reader
|
||||||
rdr.r = rawreader.ConvertReader(r)
|
var err error
|
||||||
err := rdr.Init()
|
rdr.r, err = rawreader.ConvertReader(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = rdr.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -145,7 +151,10 @@ func (r *Reader) Init() error {
|
|||||||
}
|
}
|
||||||
unread := r.super.IDCount
|
unread := r.super.IDCount
|
||||||
blockOffsets := make([]uint64, int(math.Ceil(float64(r.super.IDCount)/2048)))
|
blockOffsets := make([]uint64, int(math.Ceil(float64(r.super.IDCount)/2048)))
|
||||||
r.r.Seek(int64(r.super.IDTableStart), io.SeekStart)
|
_, err = r.r.Seek(int64(r.super.IDTableStart), io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for i := range blockOffsets {
|
for i := range blockOffsets {
|
||||||
err = binary.Read(r.r, binary.LittleEndian, &blockOffsets[i])
|
err = binary.Read(r.r, binary.LittleEndian, &blockOffsets[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -180,6 +189,7 @@ func (r *Reader) Init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
r.FS = FS{
|
r.FS = FS{
|
||||||
|
i: i,
|
||||||
r: r,
|
r: r,
|
||||||
name: "/",
|
name: "/",
|
||||||
entries: entries,
|
entries: entries,
|
||||||
|
|||||||
+3
-2
@@ -135,10 +135,11 @@ func (f File) FS() (*FS, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &FS{
|
return &FS{
|
||||||
entries: ents,
|
i: f.i,
|
||||||
parent: f.parent,
|
|
||||||
r: f.r,
|
r: f.r,
|
||||||
|
parent: f.parent,
|
||||||
name: f.name,
|
name: f.name,
|
||||||
|
entries: ents,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-2
@@ -10,11 +10,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/CalebQ42/squashfs/internal/directory"
|
"github.com/CalebQ42/squashfs/internal/directory"
|
||||||
|
"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 {
|
||||||
|
i *inode.Inode
|
||||||
r *Reader
|
r *Reader
|
||||||
parent *FS
|
parent *FS
|
||||||
name string
|
name string
|
||||||
@@ -44,6 +46,9 @@ func (f FS) Open(name string) (fs.File, error) {
|
|||||||
}
|
}
|
||||||
return f.parent.Open(strings.Join(split[1:], "/"))
|
return f.parent.Open(strings.Join(split[1:], "/"))
|
||||||
}
|
}
|
||||||
|
if split[0] == "." {
|
||||||
|
return &File{i: f.i, r: f.r, parent: f.parent, name: f.name}, nil
|
||||||
|
}
|
||||||
for i := 0; i < len(f.entries); i++ {
|
for i := 0; i < len(f.entries); i++ {
|
||||||
if match, _ := path.Match(split[0], f.entries[i].Name); match {
|
if match, _ := path.Match(split[0], f.entries[i].Name); match {
|
||||||
if len(split) == 1 {
|
if len(split) == 1 {
|
||||||
@@ -183,6 +188,10 @@ func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
|
|||||||
}
|
}
|
||||||
return f.parent.ReadDir(strings.Join(split[1:], "/"))
|
return f.parent.ReadDir(strings.Join(split[1:], "/"))
|
||||||
}
|
}
|
||||||
|
if split[0] == "." {
|
||||||
|
f := &File{i: f.i, r: f.r, parent: f.parent, name: f.name}
|
||||||
|
return f.ReadDir(-1)
|
||||||
|
}
|
||||||
for i := 0; i < len(f.entries); i++ {
|
for i := 0; i < len(f.entries); i++ {
|
||||||
if match, _ := path.Match(split[0], f.entries[i].Name); match {
|
if match, _ := path.Match(split[0], f.entries[i].Name); match {
|
||||||
if len(split) == 1 {
|
if len(split) == 1 {
|
||||||
@@ -202,7 +211,7 @@ func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
|
|||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out := make([]fs.DirEntry, len(f.entries))
|
out := make([]fs.DirEntry, len(ents))
|
||||||
for i, ent := range ents {
|
for i, ent := range ents {
|
||||||
out[i] = &DirEntry{
|
out[i] = &DirEntry{
|
||||||
en: ent,
|
en: ent,
|
||||||
@@ -299,6 +308,10 @@ func (f FS) Stat(name string) (fs.FileInfo, error) {
|
|||||||
}
|
}
|
||||||
return f.parent.Stat(strings.Join(split[1:], "/"))
|
return f.parent.Stat(strings.Join(split[1:], "/"))
|
||||||
}
|
}
|
||||||
|
if split[0] == "." {
|
||||||
|
f := &File{i: f.i, r: f.r, parent: f.parent, name: f.name}
|
||||||
|
return f.Stat()
|
||||||
|
}
|
||||||
for i := 0; i < len(f.entries); i++ {
|
for i := 0; i < len(f.entries); i++ {
|
||||||
if match, _ := path.Match(split[0], f.entries[i].Name); match {
|
if match, _ := path.Match(split[0], f.entries[i].Name); match {
|
||||||
if len(split) == 1 {
|
if len(split) == 1 {
|
||||||
@@ -382,6 +395,9 @@ func (f FS) Sub(dir string) (fs.FS, error) {
|
|||||||
}
|
}
|
||||||
return f.parent.Sub(strings.Join(split[1:], "/"))
|
return f.parent.Sub(strings.Join(split[1:], "/"))
|
||||||
}
|
}
|
||||||
|
if split[0] == "." {
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
for i := 0; i < len(f.entries); i++ {
|
for i := 0; i < len(f.entries); i++ {
|
||||||
if match, _ := path.Match(split[0], f.entries[i].Name); match {
|
if match, _ := path.Match(split[0], f.entries[i].Name); match {
|
||||||
if len(split) == 1 {
|
if len(split) == 1 {
|
||||||
@@ -401,7 +417,8 @@ func (f FS) Sub(dir string) (fs.FS, error) {
|
|||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &FS{
|
return FS{
|
||||||
|
i: in,
|
||||||
r: f.r,
|
r: f.r,
|
||||||
parent: &f,
|
parent: &f,
|
||||||
name: f.entries[i].Name,
|
name: f.entries[i].Name,
|
||||||
|
|||||||
+64
-16
@@ -14,9 +14,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
downloadURL = "https://github.com/srevinsaju/Firefox-Appimage/releases/download/firefox-v84.0.r20201221152838/firefox-84.0.r20201221152838-x86_64.AppImage"
|
appImageURL = "https://github.com/srevinsaju/Firefox-Appimage/releases/download/firefox-v84.0.r20201221152838/firefox-84.0.r20201221152838-x86_64.AppImage"
|
||||||
appImageName = "firefox-84.0.r20201221152838-x86_64.AppImage"
|
appImageName = "firefox-84.0.r20201221152838-x86_64.AppImage"
|
||||||
squashfsName = "balenaEtcher-1.5.113-x64.AppImage.sfs"
|
squashfsURL = "https://darkstorm.tech/LinuxPATest.sfs"
|
||||||
|
squashfsName = "LinuxPATest.sfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSquashfs(t *testing.T) {
|
func TestSquashfs(t *testing.T) {
|
||||||
@@ -25,6 +26,13 @@ func TestSquashfs(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
squashFil, err := os.Open(wd + "/testing/" + squashfsName)
|
squashFil, err := os.Open(wd + "/testing/" + squashfsName)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
err = downloadTestSquash(wd + "/testing")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
squashFil, err = os.Open(wd + "/testing/" + squashfsName)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -32,19 +40,33 @@ func TestSquashfs(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
fmt.Println("stuff", rdr.super.CompressionType)
|
os.RemoveAll(wd + "/testing/" + squashfsName + ".d")
|
||||||
// fil := rdr.GetFileAtPath("*.desktop")
|
op := DefaultOptions()
|
||||||
// if fil == nil {
|
op.Verbose = true
|
||||||
// t.Fatal("Can't find desktop fil")
|
err = rdr.ExtractWithOptions(wd+"/testing/"+squashfsName+".d", op)
|
||||||
// }
|
if err != nil {
|
||||||
// errs := fil.ExtractTo(wd + "/testing")
|
t.Fatal(err)
|
||||||
// if len(errs) > 0 {
|
}
|
||||||
// t.Fatal(errs)
|
t.Fatal("No Problems")
|
||||||
// }
|
}
|
||||||
// errs = rdr.ExtractTo(wd + "/testing/" + squashfsName + ".d")
|
|
||||||
// if len(errs) > 0 {
|
func TestSquashfsFromReader(t *testing.T) {
|
||||||
// t.Fatal(errs)
|
resp, err := http.DefaultClient.Get(squashfsURL)
|
||||||
// }
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
rdr, err := NewSquashfsReaderFromReader(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
os.RemoveAll("testing/" + squashfsName + ".d")
|
||||||
|
op := DefaultOptions()
|
||||||
|
op.Verbose = true
|
||||||
|
err = rdr.ExtractWithOptions("testing/"+squashfsName+".d", op)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
t.Fatal("No Problems")
|
t.Fatal("No Problems")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +193,7 @@ func downloadTestAppImage(dir string) error {
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
resp, err := check.Get(downloadURL)
|
resp, err := check.Get(appImageURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -183,6 +205,32 @@ func downloadTestAppImage(dir string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func downloadTestSquash(dir string) error {
|
||||||
|
//seems to time out on slow connections. Might fix that at some point... or not. It's just a test...
|
||||||
|
os.Mkdir(dir, os.ModePerm)
|
||||||
|
sfs, err := os.Create(dir + "/" + squashfsName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sfs.Close()
|
||||||
|
check := http.Client{
|
||||||
|
CheckRedirect: func(r *http.Request, _ []*http.Request) error {
|
||||||
|
r.URL.Opaque = r.URL.Path
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp, err := check.Get(squashfsURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
_, err = io.Copy(sfs, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateSquashFromAppImage(t *testing.T) {
|
func TestCreateSquashFromAppImage(t *testing.T) {
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user