Path: blob/main/vendor/github.com/spf13/afero/basepath.go
2875 views
package afero12import (3"io/fs"4"os"5"path/filepath"6"runtime"7"strings"8"time"9)1011var (12_ Lstater = (*BasePathFs)(nil)13_ fs.ReadDirFile = (*BasePathFile)(nil)14)1516// The BasePathFs restricts all operations to a given path within an Fs.17// The given file name to the operations on this Fs will be prepended with18// the base path before calling the base Fs.19// Any file name (after filepath.Clean()) outside this base path will be20// treated as non existing file.21//22// Note that it does not clean the error messages on return, so you may23// reveal the real path on errors.24type BasePathFs struct {25source Fs26path string27}2829type BasePathFile struct {30File31path string32}3334func (f *BasePathFile) Name() string {35sourcename := f.File.Name()36return strings.TrimPrefix(sourcename, filepath.Clean(f.path))37}3839func (f *BasePathFile) ReadDir(n int) ([]fs.DirEntry, error) {40if rdf, ok := f.File.(fs.ReadDirFile); ok {41return rdf.ReadDir(n)42}43return readDirFile{f.File}.ReadDir(n)44}4546func NewBasePathFs(source Fs, path string) Fs {47return &BasePathFs{source: source, path: path}48}4950// on a file outside the base path it returns the given file name and an error,51// else the given file with the base path prepended52func (b *BasePathFs) RealPath(name string) (path string, err error) {53if err := validateBasePathName(name); err != nil {54return name, err55}5657bpath := filepath.Clean(b.path)58path = filepath.Clean(filepath.Join(bpath, name))59if !strings.HasPrefix(path, bpath) {60return name, os.ErrNotExist61}6263return path, nil64}6566func validateBasePathName(name string) error {67if runtime.GOOS != "windows" {68// Not much to do here;69// the virtual file paths all look absolute on *nix.70return nil71}7273// On Windows a common mistake would be to provide an absolute OS path74// We could strip out the base part, but that would not be very portable.75if filepath.IsAbs(name) {76return os.ErrNotExist77}7879return nil80}8182func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) {83if name, err = b.RealPath(name); err != nil {84return &os.PathError{Op: "chtimes", Path: name, Err: err}85}86return b.source.Chtimes(name, atime, mtime)87}8889func (b *BasePathFs) Chmod(name string, mode os.FileMode) (err error) {90if name, err = b.RealPath(name); err != nil {91return &os.PathError{Op: "chmod", Path: name, Err: err}92}93return b.source.Chmod(name, mode)94}9596func (b *BasePathFs) Chown(name string, uid, gid int) (err error) {97if name, err = b.RealPath(name); err != nil {98return &os.PathError{Op: "chown", Path: name, Err: err}99}100return b.source.Chown(name, uid, gid)101}102103func (b *BasePathFs) Name() string {104return "BasePathFs"105}106107func (b *BasePathFs) Stat(name string) (fi os.FileInfo, err error) {108if name, err = b.RealPath(name); err != nil {109return nil, &os.PathError{Op: "stat", Path: name, Err: err}110}111return b.source.Stat(name)112}113114func (b *BasePathFs) Rename(oldname, newname string) (err error) {115if oldname, err = b.RealPath(oldname); err != nil {116return &os.PathError{Op: "rename", Path: oldname, Err: err}117}118if newname, err = b.RealPath(newname); err != nil {119return &os.PathError{Op: "rename", Path: newname, Err: err}120}121return b.source.Rename(oldname, newname)122}123124func (b *BasePathFs) RemoveAll(name string) (err error) {125if name, err = b.RealPath(name); err != nil {126return &os.PathError{Op: "remove_all", Path: name, Err: err}127}128return b.source.RemoveAll(name)129}130131func (b *BasePathFs) Remove(name string) (err error) {132if name, err = b.RealPath(name); err != nil {133return &os.PathError{Op: "remove", Path: name, Err: err}134}135return b.source.Remove(name)136}137138func (b *BasePathFs) OpenFile(name string, flag int, mode os.FileMode) (f File, err error) {139if name, err = b.RealPath(name); err != nil {140return nil, &os.PathError{Op: "openfile", Path: name, Err: err}141}142sourcef, err := b.source.OpenFile(name, flag, mode)143if err != nil {144return nil, err145}146return &BasePathFile{sourcef, b.path}, nil147}148149func (b *BasePathFs) Open(name string) (f File, err error) {150if name, err = b.RealPath(name); err != nil {151return nil, &os.PathError{Op: "open", Path: name, Err: err}152}153sourcef, err := b.source.Open(name)154if err != nil {155return nil, err156}157return &BasePathFile{File: sourcef, path: b.path}, nil158}159160func (b *BasePathFs) Mkdir(name string, mode os.FileMode) (err error) {161if name, err = b.RealPath(name); err != nil {162return &os.PathError{Op: "mkdir", Path: name, Err: err}163}164return b.source.Mkdir(name, mode)165}166167func (b *BasePathFs) MkdirAll(name string, mode os.FileMode) (err error) {168if name, err = b.RealPath(name); err != nil {169return &os.PathError{Op: "mkdir", Path: name, Err: err}170}171return b.source.MkdirAll(name, mode)172}173174func (b *BasePathFs) Create(name string) (f File, err error) {175if name, err = b.RealPath(name); err != nil {176return nil, &os.PathError{Op: "create", Path: name, Err: err}177}178sourcef, err := b.source.Create(name)179if err != nil {180return nil, err181}182return &BasePathFile{File: sourcef, path: b.path}, nil183}184185func (b *BasePathFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {186name, err := b.RealPath(name)187if err != nil {188return nil, false, &os.PathError{Op: "lstat", Path: name, Err: err}189}190if lstater, ok := b.source.(Lstater); ok {191return lstater.LstatIfPossible(name)192}193fi, err := b.source.Stat(name)194return fi, false, err195}196197func (b *BasePathFs) SymlinkIfPossible(oldname, newname string) error {198oldname, err := b.RealPath(oldname)199if err != nil {200return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: err}201}202newname, err = b.RealPath(newname)203if err != nil {204return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: err}205}206if linker, ok := b.source.(Linker); ok {207return linker.SymlinkIfPossible(oldname, newname)208}209return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink}210}211212func (b *BasePathFs) ReadlinkIfPossible(name string) (string, error) {213name, err := b.RealPath(name)214if err != nil {215return "", &os.PathError{Op: "readlink", Path: name, Err: err}216}217if reader, ok := b.source.(LinkReader); ok {218return reader.ReadlinkIfPossible(name)219}220return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink}221}222223224