Started working on the main library (nearly complete)

This commit is contained in:
Caleb Gardner
2023-12-24 08:05:56 -06:00
parent b2a3920c1f
commit 5de59627df
10 changed files with 550 additions and 16 deletions
+241
View File
@@ -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)
}