102 lines
1.9 KiB
Go
102 lines
1.9 KiB
Go
package storage
|
|
|
|
import (
|
|
"errors"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
type Storage interface {
|
|
Stat(path string) (os.FileInfo, error)
|
|
Open(path string) (*os.File, error)
|
|
Create(path string) (*os.File, error)
|
|
Remove(path string) error
|
|
Rename(oldPath, newPath string) error
|
|
MkdirAll(path string) error
|
|
List(path string) ([]os.FileInfo, error)
|
|
}
|
|
|
|
type Local struct{ Root string }
|
|
|
|
func (s Local) real(path string) (string, error) {
|
|
root, err := filepath.Abs(s.Root)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
p := strings.TrimPrefix(filepath.Clean("/"+path), string(filepath.Separator))
|
|
full := filepath.Join(root, p)
|
|
rel, err := filepath.Rel(root, full)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
|
return "", errors.New("path escapes storage root")
|
|
}
|
|
return full, nil
|
|
}
|
|
func (s Local) Stat(path string) (os.FileInfo, error) {
|
|
p, e := s.real(path)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
return os.Stat(p)
|
|
}
|
|
func (s Local) Open(path string) (*os.File, error) {
|
|
p, e := s.real(path)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
return os.Open(p)
|
|
}
|
|
func (s Local) Create(path string) (*os.File, error) {
|
|
p, e := s.real(path)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
if e = os.MkdirAll(filepath.Dir(p), 0755); e != nil {
|
|
return nil, e
|
|
}
|
|
return os.Create(p)
|
|
}
|
|
func (s Local) Remove(path string) error {
|
|
p, e := s.real(path)
|
|
if e != nil {
|
|
return e
|
|
}
|
|
return os.RemoveAll(p)
|
|
}
|
|
func (s Local) Rename(a, b string) error {
|
|
pa, e := s.real(a)
|
|
if e != nil {
|
|
return e
|
|
}
|
|
pb, e := s.real(b)
|
|
if e != nil {
|
|
return e
|
|
}
|
|
return os.Rename(pa, pb)
|
|
}
|
|
func (s Local) MkdirAll(path string) error {
|
|
p, e := s.real(path)
|
|
if e != nil {
|
|
return e
|
|
}
|
|
return os.MkdirAll(p, 0755)
|
|
}
|
|
func (s Local) List(path string) ([]os.FileInfo, error) {
|
|
p, e := s.real(path)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
f, e := os.Open(p)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
defer f.Close()
|
|
return f.Readdir(-1)
|
|
}
|
|
|
|
var _ fs.FS
|