2020-10-23 19:55:37 +00:00
|
|
|
package vfscopy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"io"
|
|
|
|
"path/filepath"
|
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
"errors"
|
|
|
|
)
|
|
|
|
|
2020-10-23 20:29:09 +00:00
|
|
|
// HTTPFileSystem is copied from http.FileSystem
|
|
|
|
type HTTPFileSystem interface {
|
|
|
|
Open(name string) (File, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FileSystem is a Virtual FileSystem with Symlink support
|
2020-10-23 19:55:37 +00:00
|
|
|
type FileSystem interface {
|
|
|
|
Open(name string) (File, error)
|
2020-10-23 20:29:09 +00:00
|
|
|
Readlink(name string) (string, error)
|
|
|
|
EvalSymlinks(name string) (string, error)
|
2020-10-23 19:55:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// File is copied from http.File
|
|
|
|
type File interface {
|
|
|
|
io.Closer
|
|
|
|
io.Reader
|
|
|
|
io.Seeker
|
|
|
|
Readdir(count int) ([]os.FileInfo, error)
|
|
|
|
Stat() (os.FileInfo, error)
|
|
|
|
}
|
|
|
|
|
2020-10-23 20:29:09 +00:00
|
|
|
// VFS is a virtual FileSystem with possible Symlink support
|
|
|
|
type VFS struct {
|
|
|
|
FileSystem HTTPFileSystem
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open opens a file relative to a virtual filesystem
|
|
|
|
func (v *VFS) Open(name string) (File, error) {
|
|
|
|
return v.Open(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Readlink returns a "not implemented" error,
|
|
|
|
// which is okay because it is never called for http.FileSystem.
|
|
|
|
func (v *VFS) Readlink(name string) (string, error) {
|
|
|
|
return "", errors.New("not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
// EvalSymlinks returns the original name,
|
|
|
|
// which is okay because it is never called for http.FileSystem.
|
|
|
|
func (v *VFS) EvalSymlinks(name string) (string, error) {
|
|
|
|
return name, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewVFS gives an http.FileSystem faux symlink support
|
|
|
|
func NewVFS(httpfs HTTPFileSystem) FileSystem {
|
|
|
|
return &VFS{ FileSystem: httpfs }
|
|
|
|
}
|
|
|
|
|
2020-10-23 19:55:37 +00:00
|
|
|
// Dir is an implementation of a Virtual FileSystem
|
|
|
|
type Dir string
|
|
|
|
|
|
|
|
// mapDirOpenError maps the provided non-nil error from opening name
|
|
|
|
// to a possibly better non-nil error. In particular, it turns OS-specific errors
|
|
|
|
// about opening files in non-directories into os.ErrNotExist. See Issue 18984.
|
|
|
|
func mapDirOpenError(originalErr error, name string) error {
|
|
|
|
if os.IsNotExist(originalErr) || os.IsPermission(originalErr) {
|
|
|
|
return originalErr
|
|
|
|
}
|
|
|
|
|
|
|
|
parts := strings.Split(name, string(filepath.Separator))
|
|
|
|
for i := range parts {
|
|
|
|
if parts[i] == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fi, err := os.Stat(strings.Join(parts[:i+1], string(filepath.Separator)))
|
|
|
|
if err != nil {
|
|
|
|
return originalErr
|
|
|
|
}
|
|
|
|
if !fi.IsDir() {
|
|
|
|
return os.ErrNotExist
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return originalErr
|
|
|
|
}
|
|
|
|
|
2020-10-23 20:29:09 +00:00
|
|
|
func (d Dir) fullName(name string) (string, error) {
|
2020-10-23 19:55:37 +00:00
|
|
|
if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) {
|
2020-10-23 20:29:09 +00:00
|
|
|
return "", errors.New("http: invalid character in file path")
|
2020-10-23 19:55:37 +00:00
|
|
|
}
|
|
|
|
dir := string(d)
|
|
|
|
if dir == "" {
|
|
|
|
dir = "."
|
|
|
|
}
|
|
|
|
fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))
|
2020-10-23 20:29:09 +00:00
|
|
|
return fullName, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open opens a file relative to a virtual filesystem
|
|
|
|
func (d Dir) Open(name string) (File, error) {
|
|
|
|
fullName, err := d.fullName(name)
|
|
|
|
if nil != err {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-10-23 19:55:37 +00:00
|
|
|
f, err := os.Open(fullName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, mapDirOpenError(err, fullName)
|
|
|
|
}
|
|
|
|
return f, nil
|
|
|
|
}
|
2020-10-23 20:29:09 +00:00
|
|
|
|
|
|
|
// Readlink returns the destination of the named symbolic link.
|
|
|
|
func (d Dir) Readlink(name string) (string, error) {
|
|
|
|
name, err := d.fullName(name)
|
|
|
|
if nil != err {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
name, err = os.Readlink(name)
|
|
|
|
if err != nil {
|
|
|
|
return "", mapDirOpenError(err, name)
|
|
|
|
}
|
|
|
|
return name, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// EvalSymlinks returns the destination of the named symbolic link.
|
|
|
|
func (d Dir) EvalSymlinks(name string) (string, error) {
|
|
|
|
name, err := d.fullName(name)
|
|
|
|
if nil != err {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
name, err = filepath.EvalSymlinks(name)
|
|
|
|
if err != nil {
|
|
|
|
return "", mapDirOpenError(err, name)
|
|
|
|
}
|
|
|
|
return name, nil
|
|
|
|
}
|