16ef5838c3
This is largely a move to simplify a lot of the readers Also further breaks out functions.
367 lines
7.4 KiB
Go
367 lines
7.4 KiB
Go
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) (f *FS, err error) {
|
|
f = new(FS)
|
|
f.i, err = r.inodeFromDir(e)
|
|
if err != nil {
|
|
return
|
|
}
|
|
f.r = &r
|
|
f.e, err = r.readDirectory(f.i)
|
|
return
|
|
}
|
|
|
|
//Open opens the file at name. Returns a squashfs.File.
|
|
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 = filepath.Clean(name)
|
|
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])
|
|
if err != nil {
|
|
return nil, &fs.PathError{
|
|
Op: "open",
|
|
Path: name,
|
|
Err: err,
|
|
}
|
|
}
|
|
out, err := newFS.Open(strings.Join(split[1:], "/"))
|
|
if err != nil {
|
|
err.(*fs.PathError).Path = name
|
|
}
|
|
return out, err
|
|
}
|
|
out, err := f.r.newFile(f.e[i])
|
|
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,
|
|
}
|
|
}
|
|
|
|
//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) {
|
|
if !fs.ValidPath(pattern) {
|
|
return nil, &fs.PathError{
|
|
Op: "glob",
|
|
Path: pattern,
|
|
Err: fs.ErrInvalid,
|
|
}
|
|
}
|
|
pattern = filepath.Clean(pattern)
|
|
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).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) {
|
|
if !fs.ValidPath(name) {
|
|
return nil, &fs.PathError{
|
|
Op: "readdir",
|
|
Path: name,
|
|
Err: fs.ErrInvalid,
|
|
}
|
|
}
|
|
name = filepath.Clean(name)
|
|
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])
|
|
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).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) {
|
|
if !fs.ValidPath(name) {
|
|
return nil, &fs.PathError{
|
|
Op: "stat",
|
|
Path: name,
|
|
Err: fs.ErrInvalid,
|
|
}
|
|
}
|
|
name = filepath.Clean(strings.TrimPrefix(name, "/"))
|
|
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).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) {
|
|
if !fs.ValidPath(dir) {
|
|
return nil, &fs.PathError{
|
|
Op: "sub",
|
|
Path: dir,
|
|
Err: fs.ErrInvalid,
|
|
}
|
|
}
|
|
dir = filepath.Clean(dir)
|
|
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])
|
|
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,
|
|
}
|
|
}
|