Restructure for 1.16 io/fs interface

This commit is contained in:
Caleb Gardner
2021-01-29 12:55:57 -06:00
parent dd08d3516d
commit 3f1b2a8d1e
7 changed files with 1004 additions and 690 deletions
+126
View File
@@ -0,0 +1,126 @@
package squashfs
import (
"io"
"io/fs"
"time"
"github.com/CalebQ42/squashfs/internal/directory"
"github.com/CalebQ42/squashfs/internal/inode"
)
type DirEntry struct {
en *directory.Entry
parent *FS
r *Reader
}
func (r *Reader) newDirEntry(en *directory.Entry, parent *FS) *DirEntry {
return &DirEntry{
en: en,
parent: parent,
r: r,
}
}
func (d DirEntry) Name() string {
return d.en.Name
}
func (d DirEntry) IsDir() bool {
return d.en.Type == inode.DirType
}
func (d DirEntry) Type() fs.FileMode {
switch d.en.Type {
case inode.DirType:
return fs.ModeDir
case inode.SymType:
return fs.ModeSymlink
default:
return 0
}
}
func (d DirEntry) Info() (fs.FileInfo, error) {
in, err := d.r.getInodeFromEntry(d.en)
if err != nil {
return nil, err
}
return &FileInfo{
name: d.en.Name,
i: in,
parent: d.parent,
r: d.r,
}, nil
}
//GetInodeFromEntry returns the inode associated with a given directory.Entry
func (r *Reader) getInodeFromEntry(en *directory.Entry) (*inode.Inode, error) {
br, err := r.newMetadataReader(int64(r.super.InodeTableStart + uint64(en.InodeOffset)))
if err != nil {
return nil, err
}
_, err = br.Seek(int64(en.InodeBlockOffset), io.SeekStart)
if err != nil {
return nil, err
}
i, err := inode.ProcessInode(br, r.super.BlockSize)
if err != nil {
return nil, err
}
return i, nil
}
type FileInfo struct {
i *inode.Inode
parent *FS
r *Reader
name string
}
func (f FileInfo) Name() string {
return f.name
}
func (f FileInfo) Size() int64 {
switch f.i.Type {
case inode.FileType:
return int64(f.i.Info.(inode.File).Size)
case inode.ExtFileType:
return int64(f.i.Info.(inode.ExtFile).Size)
}
return 0
}
func (f FileInfo) Mode() fs.FileMode {
mode := fs.FileMode(f.i.Permissions)
switch f.i.Type {
case inode.DirType | inode.ExtDirType:
return mode | fs.ModeDir
case inode.ExtDirType:
return mode | fs.ModeDir
case inode.SymType:
return mode | fs.ModeSymlink
case inode.ExtSymType:
return mode | fs.ModeSymlink
}
return mode
}
func (f FileInfo) ModTime() time.Time {
return time.Unix(int64(f.i.ModifiedTime), 0)
}
func (f FileInfo) IsDir() bool {
return f.i.Type == inode.DirType || f.i.Type == inode.ExtDirType
}
func (f FileInfo) Sys() interface{} {
return &File{
name: f.name,
i: f.i,
r: f.r,
parent: f.parent,
}
}
+493 -543
View File
File diff suppressed because it is too large Load Diff
+157
View File
@@ -0,0 +1,157 @@
package squashfs
import (
"errors"
"io"
"io/fs"
"github.com/CalebQ42/squashfs/internal/directory"
"github.com/CalebQ42/squashfs/internal/inode"
)
type File struct {
i *inode.Inode
parent *FS
r *Reader
reader *fileReader
name string
dirsRead int
}
func (f FileInfo) File() (file *File, err error) {
file = &File{
name: f.name,
r: f.r,
parent: f.parent,
i: f.i,
}
file.reader, err = f.r.newFileReader(f.i)
return
}
func (r *Reader) newFileFromDirEntry(en *directory.Entry, parent *FS) (file *File, err error) {
file = &File{
name: en.Name,
r: r,
parent: parent,
}
file.i, err = r.getInodeFromEntry(en)
if err != nil {
return nil, err
}
file.reader, err = r.newFileReader(file.i)
return
}
func (f *File) Stat() (fs.FileInfo, error) {
return &FileInfo{
i: f.i,
name: f.name,
parent: f.parent,
r: f.r,
}, nil
}
func (f *File) Read(p []byte) (int, error) {
if f.i.Type == inode.FileType || f.i.Type == inode.ExtFileType {
if f.reader == nil {
return 0, fs.ErrClosed
}
return f.reader.Read(p)
}
return 0, errors.New("Can only read files")
}
func (f *File) WriteTo(w io.Writer) (int64, error) {
if f.i.Type == inode.FileType || f.i.Type == inode.ExtFileType {
if f.reader == nil {
return 0, fs.ErrClosed
}
return f.reader.WriteTo(w)
}
return 0, errors.New("Can only read files")
}
func (f *File) Close() error {
f.reader = nil
return nil
}
func (f *File) ReadDir(n int) ([]fs.DirEntry, error) {
if !f.IsDir() {
return nil, errors.New("File is not a directory")
}
ffs, err := f.FS()
if err != nil {
return nil, err
}
var beg, end int
if n <= 0 {
beg, end = 0, len(ffs.entries)
} else {
beg, end = f.dirsRead, f.dirsRead+n
if end > len(ffs.entries) {
end = len(ffs.entries)
err = io.EOF
}
}
out := make([]fs.DirEntry, end-beg)
for i, ent := range ffs.entries[beg:end] {
out[i] = f.r.newDirEntry(ent, ffs)
}
return out, err
}
func (f File) FS() (*FS, error) {
if !f.IsDir() {
return nil, errors.New("File is not a directory")
}
ents, err := f.r.readDirFromInode(f.i)
if err != nil {
return nil, err
}
return &FS{
entries: ents,
parent: f.parent,
r: f.r,
}, nil
}
func (f File) IsDir() bool {
return f.i.Type == inode.DirType || f.i.Type == inode.ExtDirType
}
func (f File) Path()
//ReadDirFromInode returns a fully populated Directory from a given Inode.
//If the given inode is not a directory it returns an error.
func (r *Reader) readDirFromInode(i *inode.Inode) ([]*directory.Entry, error) {
var offset uint32
var metaOffset uint16
var size uint32
switch i.Type {
case inode.DirType:
offset = i.Info.(inode.Dir).DirectoryIndex
metaOffset = i.Info.(inode.Dir).DirectoryOffset
size = uint32(i.Info.(inode.Dir).DirectorySize)
case inode.ExtDirType:
offset = i.Info.(inode.ExtDir).DirectoryIndex
metaOffset = i.Info.(inode.ExtDir).DirectoryOffset
size = i.Info.(inode.ExtDir).DirectorySize
default:
return nil, errors.New("Not a directory inode")
}
br, err := r.newMetadataReader(int64(r.super.DirTableStart + uint64(offset)))
if err != nil {
return nil, err
}
_, err = br.Seek(int64(metaOffset), io.SeekStart)
if err != nil {
return nil, err
}
ents, err := directory.NewDirectory(br, size)
if err != nil {
return nil, err
}
return ents, nil
}
+89
View File
@@ -0,0 +1,89 @@
package squashfs
import (
"io/fs"
"path"
"strings"
"github.com/CalebQ42/squashfs/internal/directory"
)
type FS struct {
r *Reader
parent *FS
entries []*directory.Entry
}
func (f FS) Open(name string) (fs.File, error) {
if !fs.ValidPath(name) {
return nil, &fs.PathError{
Op: "open",
Path: name,
Err: fs.ErrInvalid,
}
}
name = path.Clean(strings.TrimPrefix(name, "/"))
split := strings.Split(name, "/")
for i := 0; i < len(f.entries); i++ {
if match, _ := path.Match(split[0], f.entries[i].Name); match {
if len(split) == 1 {
return f.r.newFileFromDirEntry(f.entries[i], &f)
}
sub, err := f.Sub(split[0])
if err != nil {
if pathErr, ok := err.(*fs.PathError); ok {
pathErr.Op = "open"
pathErr.Path = name
return nil, err
}
return nil, &fs.PathError{
Op: "open",
Path: name,
Err: err,
}
}
fil, err := sub.Open(strings.Join(split[1:], "/"))
if err != nil {
if pathErr, ok := err.(*fs.PathError); ok {
if pathErr.Err == fs.ErrNotExist {
continue
}
pathErr.Op = "open"
pathErr.Path = name
return nil, err
}
return nil, &fs.PathError{
Op: "open",
Path: name,
Err: err,
}
}
return fil, nil
}
}
return nil, &fs.PathError{
Op: "open",
Path: name,
Err: fs.ErrNotExist,
}
}
func (f FS) Glob(pattern string) ([]string, error) {
return nil, nil
}
func (f FS) ReadDir(name string) ([]DirEntry, error) {
return nil, nil
}
func (f FS) ReadFile(name string) ([]byte, error) {
return nil, nil
}
func (f FS) Stat(name string) ([]byte, error) {
return nil, nil
}
func (f FS) Sub(dir string) (fs.FS, error) {
return nil, nil
}
+22 -29
View File
@@ -23,38 +23,33 @@ type EntryRaw struct {
//Entry is an entry in a directory.
type Entry struct {
*Header
Name string
EntryRaw
Name string
InodeOffset uint32
InodeBlockOffset int16
Type uint16
}
//NewEntry creates a new directory entry
func NewEntry(rdr io.Reader) (Entry, error) {
var entry Entry
err := binary.Read(rdr, binary.LittleEndian, &entry.EntryRaw)
func NewEntry(rdr io.Reader) (*Entry, error) {
var raw EntryRaw
err := binary.Read(rdr, binary.LittleEndian, &raw)
if err != nil {
return Entry{}, err
return nil, err
}
tmp := make([]byte, entry.EntryRaw.NameSize+1)
tmp := make([]byte, raw.NameSize+1)
err = binary.Read(rdr, binary.LittleEndian, &tmp)
if err != nil {
return Entry{}, err
return nil, err
}
entry.Name = string(tmp)
return entry, err
}
//Directory is an entry in the directory table of a squashfs.
//Will only have multiple headers if there are more then 256 entries
type Directory struct {
Headers []Header
Entries []Entry
return &Entry{
InodeBlockOffset: raw.InodeOffset,
Type: raw.Type,
Name: string(tmp),
}, nil
}
//NewDirectory reads the directory from rdr
func NewDirectory(base io.Reader, size uint32) (*Directory, error) {
var dir Directory
var err error
func NewDirectory(base io.Reader, size uint32) (entries []*Entry, err error) {
tmp := make([]byte, size)
base.Read(tmp)
rdr := bytes.NewBuffer(tmp)
@@ -62,6 +57,7 @@ func NewDirectory(base io.Reader, size uint32) (*Directory, error) {
var hdr Header
err = binary.Read(rdr, binary.LittleEndian, &hdr)
if err == io.ErrUnexpectedEOF {
err = nil
break
} else if err != nil {
return nil, err
@@ -71,24 +67,21 @@ func NewDirectory(base io.Reader, size uint32) (*Directory, error) {
if hdr.Count%256 > 0 {
headers++
}
dir.Headers = append(dir.Headers, hdr)
for i := uint32(0); i < hdr.Count; i++ {
if i != 0 && i%256 == 0 {
var newHdr Header
err = binary.Read(rdr, binary.LittleEndian, &newHdr)
err = binary.Read(rdr, binary.LittleEndian, &hdr)
if err != nil {
return nil, err
}
dir.Headers = append(dir.Headers, newHdr)
}
var ent Entry
var ent *Entry
ent, err = NewEntry(rdr)
if err != nil {
return nil, err
}
ent.Header = &dir.Headers[len(dir.Headers)-1]
dir.Entries = append(dir.Entries, ent)
ent.InodeOffset = hdr.InodeOffset
entries = append(entries, ent)
}
}
return &dir, nil
return
}
+96 -97
View File
@@ -8,7 +8,6 @@ import (
"time"
"github.com/CalebQ42/squashfs/internal/compression"
"github.com/CalebQ42/squashfs/internal/inode"
)
const (
@@ -159,117 +158,117 @@ func (r *Reader) ModTime() time.Time {
}
//ExtractTo tries to extract ALL files to the given path. This is the same as getting the root folder and extracting that.
func (r *Reader) ExtractTo(path string) []error {
if r.root == nil {
_, err := r.GetRootFolder()
if err != nil {
return []error{err}
}
}
return r.root.ExtractTo(path)
}
// func (r *Reader) ExtractTo(path string) []error {
// if r.root == nil {
// _, err := r.GetRootFolder()
// if err != nil {
// return []error{err}
// }
// }
// return r.root.ExtractTo(path)
// }
//GetRootFolder returns a squashfs.File that references the root directory of the squashfs archive.
func (r *Reader) GetRootFolder() (*File, error) {
if r.root != nil {
return r.root, nil
}
mr, err := r.newMetadataReaderFromInodeRef(r.super.RootInodeRef)
if err != nil {
return nil, err
}
// mr, err := r.newMetadataReaderFromInodeRef(r.super.RootInodeRef)
// if err != nil {
// return nil, err
// }
var root File
root.in, err = inode.ProcessInode(mr, r.super.BlockSize)
if err != nil {
return nil, err
}
root.dir = "/"
root.filType = root.in.Type
root.r = r
// root.in, err = inode.ProcessInode(mr, r.super.BlockSize)
// if err != nil {
// return nil, err
// }
// root.dir = "/"
// root.filType = root.in.Type
// root.r = r
r.root = &root
return r.root, nil
}
//GetAllFiles returns a slice of ALL files and folders contained in the squashfs.
func (r *Reader) GetAllFiles() (fils []*File, err error) {
if r.root == nil {
_, err := r.GetRootFolder()
if err != nil {
return nil, err
}
}
return r.root.GetChildrenRecursively()
}
// func (r *Reader) GetAllFiles() (fils []*File, err error) {
// if r.root == nil {
// _, err := r.GetRootFolder()
// if err != nil {
// return nil, err
// }
// }
// return r.root.GetChildrenRecursively()
// }
//FindFile returns the first file (in the same order as Reader.GetAllFiles) that the given function returns true for. Returns nil if nothing is found.
func (r *Reader) FindFile(query func(*File) bool) *File {
if r.root == nil {
_, err := r.GetRootFolder()
if err != nil {
return nil
}
}
fils, err := r.root.GetChildren()
if err != nil {
return nil
}
var childrenDirs []*File
for _, fil := range fils {
if query(fil) {
return fil
}
if fil.IsDir() {
childrenDirs = append(childrenDirs, fil)
}
}
for len(childrenDirs) != 0 {
var tmp []*File
for _, dirs := range childrenDirs {
chil, err := dirs.GetChildren()
if err != nil {
return nil
}
for _, child := range chil {
if query(child) {
return child
}
if child.IsDir() {
tmp = append(tmp, child)
}
}
}
childrenDirs = tmp
}
return nil
}
// func (r *Reader) FindFile(query func(*File) bool) *File {
// if r.root == nil {
// _, err := r.GetRootFolder()
// if err != nil {
// return nil
// }
// }
// fils, err := r.root.GetChildren()
// if err != nil {
// return nil
// }
// var childrenDirs []*File
// for _, fil := range fils {
// if query(fil) {
// return fil
// }
// if fil.IsDir() {
// childrenDirs = append(childrenDirs, fil)
// }
// }
// for len(childrenDirs) != 0 {
// var tmp []*File
// for _, dirs := range childrenDirs {
// chil, err := dirs.GetChildren()
// if err != nil {
// return nil
// }
// for _, child := range chil {
// if query(child) {
// return child
// }
// if child.IsDir() {
// tmp = append(tmp, child)
// }
// }
// }
// childrenDirs = tmp
// }
// return nil
// }
//FindAll returns all files where the given function returns true.
func (r *Reader) FindAll(query func(*File) bool) (all []*File) {
if r.root == nil {
_, err := r.GetRootFolder()
if err != nil {
return nil
}
}
fils, err := r.root.GetChildrenRecursively()
if err != nil {
return nil
}
for _, fil := range fils {
if query(fil) {
all = append(all, fil)
}
}
return
}
// func (r *Reader) FindAll(query func(*File) bool) (all []*File) {
// if r.root == nil {
// _, err := r.GetRootFolder()
// if err != nil {
// return nil
// }
// }
// fils, err := r.root.GetChildrenRecursively()
// if err != nil {
// return nil
// }
// for _, fil := range fils {
// if query(fil) {
// all = append(all, fil)
// }
// }
// return
// }
//GetFileAtPath will return the file at the given path. If the file cannot be found, will return nil.
func (r *Reader) GetFileAtPath(filepath string) *File {
if r.root == nil {
_, err := r.GetRootFolder()
if err != nil {
return nil
}
}
return r.root.GetFileAtPath(filepath)
}
// func (r *Reader) GetFileAtPath(filepath string) *File {
// if r.root == nil {
// _, err := r.GetRootFolder()
// if err != nil {
// return nil
// }
// }
// return r.root.GetFileAtPath(filepath)
// }
+21 -21
View File
@@ -34,18 +34,18 @@ func TestSquashfs(t *testing.T) {
t.Fatal(err)
}
fmt.Println("stuff", rdr.super.CompressionType)
fil := rdr.GetFileAtPath("*.desktop")
if fil == nil {
t.Fatal("Can't find desktop fil")
}
errs := fil.ExtractTo(wd + "/testing")
if len(errs) > 0 {
t.Fatal(errs)
}
errs = rdr.ExtractTo(wd + "/testing/" + squashfsName + ".d")
if len(errs) > 0 {
t.Fatal(errs)
}
// fil := rdr.GetFileAtPath("*.desktop")
// if fil == nil {
// t.Fatal("Can't find desktop fil")
// }
// errs := fil.ExtractTo(wd + "/testing")
// if len(errs) > 0 {
// t.Fatal(errs)
// }
// errs = rdr.ExtractTo(wd + "/testing/" + squashfsName + ".d")
// if len(errs) > 0 {
// t.Fatal(errs)
// }
t.Fatal("No Problems")
}
@@ -131,7 +131,7 @@ func BenchmarkDragRace(b *testing.B) {
} else if err != nil {
b.Fatal(err)
}
stat, _ := aiFil.Stat()
// stat, _ := aiFil.Stat()
ai := goappimage.NewAppImage(wd + "/testing/" + appImageName)
os.RemoveAll(wd + "/testing/unsquashFirefox")
os.RemoveAll(wd + "/testing/firefox")
@@ -143,14 +143,14 @@ func BenchmarkDragRace(b *testing.B) {
}
unsquashTime := time.Since(start)
start = time.Now()
rdr, err := NewSquashfsReader(io.NewSectionReader(aiFil, ai.Offset, stat.Size()-ai.Offset))
if err != nil {
b.Fatal(err)
}
errs := rdr.ExtractTo(wd + "/testing/firefox")
if len(errs) > 0 {
b.Fatal(errs)
}
// rdr, err := NewSquashfsReader(io.NewSectionReader(aiFil, ai.Offset, stat.Size()-ai.Offset))
// if err != nil {
// b.Fatal(err)
// }
// errs := rdr.ExtractTo(wd + "/testing/firefox")
// if len(errs) > 0 {
// b.Fatal(errs)
// }
libTime := time.Since(start)
b.Log("Unsqushfs:", unsquashTime.Round(time.Millisecond))
b.Log("Library:", libTime.Round(time.Millisecond))