2023-07-27 22:33:43 +00:00
|
|
|
package vfs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type FS struct {
|
|
|
|
directory *directory
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewVFS() *FS {
|
|
|
|
return &FS{
|
|
|
|
directory: &directory{
|
|
|
|
children: make(map[string]interface{}),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FS) Open(path string) (fs.File, error) {
|
|
|
|
if !fs.ValidPath(path) {
|
|
|
|
return nil, &fs.PathError{
|
|
|
|
Op: "Open",
|
|
|
|
Path: path,
|
|
|
|
Err: fs.ErrInvalid,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if path == "." {
|
|
|
|
path = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
child, err := f.find(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch c := child.(type) {
|
|
|
|
case *File:
|
2023-07-27 22:56:09 +00:00
|
|
|
return &File{
|
2023-07-27 22:33:43 +00:00
|
|
|
name: c.name,
|
|
|
|
mode: c.mode,
|
|
|
|
data: bytes.NewBuffer(c.data.Bytes()),
|
|
|
|
open: true,
|
2023-07-27 22:56:09 +00:00
|
|
|
}, nil
|
2023-07-27 22:33:43 +00:00
|
|
|
case *directory:
|
|
|
|
return &directoryHandle{
|
|
|
|
dir: c,
|
|
|
|
}, nil
|
2023-07-27 22:56:09 +00:00
|
|
|
case *MockFile:
|
|
|
|
return &MockFile{
|
|
|
|
name: c.name,
|
|
|
|
mode: c.mode,
|
|
|
|
modified: c.modified,
|
|
|
|
open: true,
|
|
|
|
size: c.size,
|
|
|
|
}, nil
|
2023-07-27 22:33:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("%s is unknown file type %v", path, fs.ErrInvalid)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FS) find(path string) (interface{}, error) {
|
|
|
|
if path == "" {
|
|
|
|
return f.directory, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
current := f.directory
|
|
|
|
split := strings.Split(path, "/")
|
|
|
|
var target interface{}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
err = nil
|
|
|
|
|
|
|
|
for i, subpath := range split {
|
|
|
|
target, err = func() (interface{}, error) {
|
|
|
|
current.mutex.Lock()
|
|
|
|
defer current.mutex.Unlock()
|
|
|
|
|
|
|
|
child := current.children[subpath]
|
|
|
|
if child == nil {
|
|
|
|
return nil, fmt.Errorf("%s is not a directory", subpath)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := child.(*File); ok {
|
|
|
|
if i == len(split)-1 {
|
|
|
|
return child, nil
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("%s does not exist", path)
|
|
|
|
}
|
|
|
|
|
2023-07-27 22:56:09 +00:00
|
|
|
if _, ok := child.(*MockFile); ok {
|
|
|
|
if i == len(split)-1 {
|
|
|
|
return child, nil
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("%s does not exist", path)
|
|
|
|
}
|
|
|
|
|
2023-07-27 22:33:43 +00:00
|
|
|
if subdir, ok := child.(*directory); !ok {
|
|
|
|
return nil, fmt.Errorf("directory %s does not exist", path)
|
|
|
|
} else {
|
|
|
|
current = subdir
|
|
|
|
}
|
|
|
|
|
|
|
|
return child, nil
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
return target, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FS) findDirectory(path string) (*directory, error) {
|
|
|
|
if path == "" {
|
|
|
|
return f.directory, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
current := f.directory
|
|
|
|
split := strings.Split(path, "/")
|
|
|
|
|
|
|
|
for _, subpath := range split {
|
|
|
|
err := func() error {
|
|
|
|
current.mutex.Lock()
|
|
|
|
defer current.mutex.Unlock()
|
|
|
|
|
|
|
|
child := current.children[subpath]
|
|
|
|
if child == nil {
|
|
|
|
return fmt.Errorf("%s is not a directory", subpath)
|
|
|
|
}
|
|
|
|
|
|
|
|
if subdir, ok := child.(*directory); !ok {
|
|
|
|
return fmt.Errorf("directory %s does not exist", path)
|
|
|
|
} else {
|
|
|
|
current = subdir
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return current, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FS) create(createPath string) (*File, error) {
|
|
|
|
if !fs.ValidPath(createPath) {
|
|
|
|
return nil, &fs.PathError{
|
|
|
|
Op: "create",
|
|
|
|
Path: createPath,
|
|
|
|
Err: fs.ErrInvalid,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if createPath == "." {
|
|
|
|
createPath = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
dirName, fileName := path.Split(createPath)
|
|
|
|
dirName = strings.TrimSuffix(dirName, "/")
|
|
|
|
|
|
|
|
dir, err := f.findDirectory(dirName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dir.mutex.Lock()
|
|
|
|
defer dir.mutex.Unlock()
|
|
|
|
|
|
|
|
checkExist := dir.children[fileName]
|
|
|
|
if checkExist != nil {
|
|
|
|
if _, ok := checkExist.(*File); !ok {
|
|
|
|
return nil, fmt.Errorf("%s is a directory that already exists", createPath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
file := &File{
|
|
|
|
name: fileName,
|
|
|
|
mode: 0666,
|
|
|
|
data: &bytes.Buffer{},
|
|
|
|
}
|
|
|
|
dir.children[fileName] = file
|
|
|
|
return file, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FS) MkdirAll(path string, mode os.FileMode) error {
|
|
|
|
if !fs.ValidPath(path) {
|
|
|
|
return &fs.PathError{
|
|
|
|
Op: "MkdirAll",
|
|
|
|
Path: path,
|
|
|
|
Err: fs.ErrInvalid,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if path == "." {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
split := strings.Split(path, "/")
|
|
|
|
next := f.directory
|
|
|
|
|
|
|
|
for _, subpath := range split {
|
|
|
|
current := next
|
|
|
|
current.mutex.Lock()
|
|
|
|
|
|
|
|
child := current.children[subpath]
|
|
|
|
if child == nil {
|
|
|
|
newDir := &directory{
|
|
|
|
name: subpath,
|
|
|
|
mode: mode,
|
|
|
|
children: make(map[string]interface{}),
|
|
|
|
}
|
|
|
|
current.children[subpath] = newDir
|
|
|
|
next = newDir
|
|
|
|
} else {
|
|
|
|
if childDir, ok := child.(*directory); !ok {
|
|
|
|
current.mutex.Unlock()
|
|
|
|
return fmt.Errorf("%s is not a directory", subpath)
|
|
|
|
} else {
|
|
|
|
next = childDir
|
|
|
|
}
|
|
|
|
}
|
|
|
|
current.mutex.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-27 22:56:09 +00:00
|
|
|
func (f *FS) CreateMockFile(fpath string, size int64, mode os.FileMode) error {
|
|
|
|
if !fs.ValidPath(fpath) {
|
|
|
|
return &fs.PathError{
|
|
|
|
Op: "WriteFile",
|
|
|
|
Path: fpath,
|
|
|
|
Err: fs.ErrInvalid,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if fpath == "." {
|
|
|
|
fpath = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
err := func() error {
|
|
|
|
dirName, fileName := path.Split(fpath)
|
|
|
|
dirName = strings.TrimSuffix(dirName, "/")
|
|
|
|
|
|
|
|
dir, err := f.findDirectory(dirName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
dir.mutex.Lock()
|
|
|
|
defer dir.mutex.Unlock()
|
|
|
|
|
|
|
|
checkExist := dir.children[fileName]
|
|
|
|
if checkExist != nil {
|
|
|
|
if _, ok := checkExist.(*File); !ok {
|
|
|
|
return fmt.Errorf("%s is a directory that already exists", fpath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
file := &MockFile{
|
|
|
|
name: fileName,
|
|
|
|
mode: 0666,
|
|
|
|
size: size,
|
|
|
|
}
|
|
|
|
dir.children[fileName] = file
|
|
|
|
return nil
|
|
|
|
}()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-27 22:33:43 +00:00
|
|
|
func (f *FS) WriteFile(path string, data []byte, mode os.FileMode) error {
|
|
|
|
if !fs.ValidPath(path) {
|
|
|
|
return &fs.PathError{
|
|
|
|
Op: "WriteFile",
|
|
|
|
Path: path,
|
|
|
|
Err: fs.ErrInvalid,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if path == "." {
|
|
|
|
path = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := f.create(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
file.data = bytes.NewBuffer(data)
|
|
|
|
file.mode = mode
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FS) LoadDirectory(fsPath, sourcePath string) error {
|
|
|
|
err := f.MkdirAll(fsPath, 0777)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
files, err := os.ReadDir(sourcePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, file := range files {
|
|
|
|
if !file.IsDir() {
|
|
|
|
fp, err := os.Open(path.Join(sourcePath, file.Name()))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
data, err := io.ReadAll(fp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = f.WriteFile(path.Join(fsPath, file.Name()), data, 0666)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = fp.Close()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|