Started working on the main library (nearly complete)
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
package squashfs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/CalebQ42/squashfs/squashfs"
|
||||
"github.com/CalebQ42/squashfs/squashfs/directory"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
d *squashfs.Directory
|
||||
r *Reader
|
||||
parent *FS
|
||||
}
|
||||
|
||||
// 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.d.Entries); i++ {
|
||||
if match, _ := path.Match(split[0], f.d.Entries[i].Name); match {
|
||||
if len(split) == 1 {
|
||||
out = append(out, f.d.Entries[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.d.Name + "/" + subGlob[i]
|
||||
}
|
||||
out = append(out, subGlob...)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Opens the file at name. Returns a *File as an fs.File.
|
||||
func (f *FS) Open(name string) (fs.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 &File{
|
||||
b: &f.d.Base,
|
||||
r: f.r,
|
||||
parent: f.parent,
|
||||
}, nil
|
||||
}
|
||||
split := strings.Split(name, "/")
|
||||
i, found := slices.BinarySearchFunc(f.d.Entries, split[0], func(e directory.Entry, name string) int {
|
||||
return strings.Compare(e.Name, name)
|
||||
})
|
||||
if !found {
|
||||
return nil, &fs.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: fs.ErrNotExist,
|
||||
}
|
||||
}
|
||||
b, err := f.r.r.BaseFromEntry(f.d.Entries[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(split) == 1 {
|
||||
return &File{
|
||||
b: b,
|
||||
r: f.r,
|
||||
parent: f.parent,
|
||||
}, nil
|
||||
}
|
||||
if !b.IsDir() {
|
||||
return nil, &fs.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: fs.ErrNotExist,
|
||||
}
|
||||
}
|
||||
d, err := b.ToDir(f.r.r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (&FS{
|
||||
d: d,
|
||||
r: f.r,
|
||||
parent: f,
|
||||
}).Open(strings.Join(split[1:], "/"))
|
||||
}
|
||||
|
||||
// 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 (&File{
|
||||
b: &f.d.Base,
|
||||
parent: f.parent,
|
||||
r: f.r,
|
||||
}).ReadDir(-1)
|
||||
}
|
||||
fil, err := f.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fil.(*File).ReadDir(-1)
|
||||
}
|
||||
|
||||
// Returns the contents of the file at name.
|
||||
func (f *FS) ReadFile(name string) (out []byte, err error) {
|
||||
name = filepath.Clean(name)
|
||||
if !fs.ValidPath(name) {
|
||||
return nil, &fs.PathError{
|
||||
Op: "readfile",
|
||||
Path: name,
|
||||
Err: fs.ErrInvalid,
|
||||
}
|
||||
}
|
||||
if name == "." || name == "" {
|
||||
return nil, fs.ErrInvalid
|
||||
}
|
||||
fil, err := f.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !fil.(*File).IsRegular() {
|
||||
return nil, fs.ErrInvalid
|
||||
}
|
||||
return io.ReadAll(fil)
|
||||
}
|
||||
|
||||
// Returns the fs.FileInfo for the file at name.
|
||||
func (f *FS) Stat(name string) (fs.FileInfo, error) {
|
||||
name = filepath.Clean(name)
|
||||
if !fs.ValidPath(name) {
|
||||
return nil, &fs.PathError{
|
||||
Op: "stat",
|
||||
Path: name,
|
||||
Err: fs.ErrInvalid,
|
||||
}
|
||||
}
|
||||
if name == "." || name == "" {
|
||||
return (&File{
|
||||
b: &f.d.Base,
|
||||
parent: f.parent,
|
||||
r: f.r,
|
||||
}).Stat()
|
||||
}
|
||||
fil, err := f.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fil.(*File).Stat()
|
||||
}
|
||||
|
||||
// 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: "dir",
|
||||
Path: dir,
|
||||
Err: fs.ErrInvalid,
|
||||
}
|
||||
}
|
||||
if dir == "." || dir == "" {
|
||||
return f, nil
|
||||
}
|
||||
fil, err := f.Open(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !fil.(*File).IsDir() {
|
||||
return nil, &fs.PathError{
|
||||
Op: "dir",
|
||||
Path: dir,
|
||||
Err: fs.ErrInvalid,
|
||||
}
|
||||
}
|
||||
return fil.(*File).FS()
|
||||
}
|
||||
|
||||
func (f *FS) path() string {
|
||||
return filepath.Join(f.parent.path(), f.d.Name)
|
||||
}
|
||||
Reference in New Issue
Block a user