gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

basepath.go (6274B)


      1 package afero
      2 
      3 import (
      4 	"io/fs"
      5 	"os"
      6 	"path/filepath"
      7 	"runtime"
      8 	"strings"
      9 	"time"
     10 )
     11 
     12 var (
     13 	_ Lstater        = (*BasePathFs)(nil)
     14 	_ fs.ReadDirFile = (*BasePathFile)(nil)
     15 )
     16 
     17 // The BasePathFs restricts all operations to a given path within an Fs.
     18 // The given file name to the operations on this Fs will be prepended with
     19 // the base path before calling the base Fs.
     20 // Any file name (after filepath.Clean()) outside this base path will be
     21 // treated as non existing file.
     22 //
     23 // Note that it does not clean the error messages on return, so you may
     24 // reveal the real path on errors.
     25 type BasePathFs struct {
     26 	source Fs
     27 	path   string
     28 }
     29 
     30 type BasePathFile struct {
     31 	File
     32 	path string
     33 }
     34 
     35 func (f *BasePathFile) Name() string {
     36 	sourcename := f.File.Name()
     37 	return strings.TrimPrefix(sourcename, filepath.Clean(f.path))
     38 }
     39 
     40 func (f *BasePathFile) ReadDir(n int) ([]fs.DirEntry, error) {
     41 	if rdf, ok := f.File.(fs.ReadDirFile); ok {
     42 		return rdf.ReadDir(n)
     43 	}
     44 	return readDirFile{f.File}.ReadDir(n)
     45 }
     46 
     47 func NewBasePathFs(source Fs, path string) Fs {
     48 	return &BasePathFs{source: source, path: path}
     49 }
     50 
     51 // on a file outside the base path it returns the given file name and an error,
     52 // else the given file with the base path prepended
     53 func (b *BasePathFs) RealPath(name string) (path string, err error) {
     54 	if err := validateBasePathName(name); err != nil {
     55 		return name, err
     56 	}
     57 
     58 	bpath := filepath.Clean(b.path)
     59 	path = filepath.Clean(filepath.Join(bpath, name))
     60 	if !strings.HasPrefix(path, bpath) {
     61 		return name, os.ErrNotExist
     62 	}
     63 
     64 	return path, nil
     65 }
     66 
     67 func validateBasePathName(name string) error {
     68 	if runtime.GOOS != "windows" {
     69 		// Not much to do here;
     70 		// the virtual file paths all look absolute on *nix.
     71 		return nil
     72 	}
     73 
     74 	// On Windows a common mistake would be to provide an absolute OS path
     75 	// We could strip out the base part, but that would not be very portable.
     76 	if filepath.IsAbs(name) {
     77 		return os.ErrNotExist
     78 	}
     79 
     80 	return nil
     81 }
     82 
     83 func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) {
     84 	if name, err = b.RealPath(name); err != nil {
     85 		return &os.PathError{Op: "chtimes", Path: name, Err: err}
     86 	}
     87 	return b.source.Chtimes(name, atime, mtime)
     88 }
     89 
     90 func (b *BasePathFs) Chmod(name string, mode os.FileMode) (err error) {
     91 	if name, err = b.RealPath(name); err != nil {
     92 		return &os.PathError{Op: "chmod", Path: name, Err: err}
     93 	}
     94 	return b.source.Chmod(name, mode)
     95 }
     96 
     97 func (b *BasePathFs) Chown(name string, uid, gid int) (err error) {
     98 	if name, err = b.RealPath(name); err != nil {
     99 		return &os.PathError{Op: "chown", Path: name, Err: err}
    100 	}
    101 	return b.source.Chown(name, uid, gid)
    102 }
    103 
    104 func (b *BasePathFs) Name() string {
    105 	return "BasePathFs"
    106 }
    107 
    108 func (b *BasePathFs) Stat(name string) (fi os.FileInfo, err error) {
    109 	if name, err = b.RealPath(name); err != nil {
    110 		return nil, &os.PathError{Op: "stat", Path: name, Err: err}
    111 	}
    112 	return b.source.Stat(name)
    113 }
    114 
    115 func (b *BasePathFs) Rename(oldname, newname string) (err error) {
    116 	if oldname, err = b.RealPath(oldname); err != nil {
    117 		return &os.PathError{Op: "rename", Path: oldname, Err: err}
    118 	}
    119 	if newname, err = b.RealPath(newname); err != nil {
    120 		return &os.PathError{Op: "rename", Path: newname, Err: err}
    121 	}
    122 	return b.source.Rename(oldname, newname)
    123 }
    124 
    125 func (b *BasePathFs) RemoveAll(name string) (err error) {
    126 	if name, err = b.RealPath(name); err != nil {
    127 		return &os.PathError{Op: "remove_all", Path: name, Err: err}
    128 	}
    129 	return b.source.RemoveAll(name)
    130 }
    131 
    132 func (b *BasePathFs) Remove(name string) (err error) {
    133 	if name, err = b.RealPath(name); err != nil {
    134 		return &os.PathError{Op: "remove", Path: name, Err: err}
    135 	}
    136 	return b.source.Remove(name)
    137 }
    138 
    139 func (b *BasePathFs) OpenFile(name string, flag int, mode os.FileMode) (f File, err error) {
    140 	if name, err = b.RealPath(name); err != nil {
    141 		return nil, &os.PathError{Op: "openfile", Path: name, Err: err}
    142 	}
    143 	sourcef, err := b.source.OpenFile(name, flag, mode)
    144 	if err != nil {
    145 		return nil, err
    146 	}
    147 	return &BasePathFile{sourcef, b.path}, nil
    148 }
    149 
    150 func (b *BasePathFs) Open(name string) (f File, err error) {
    151 	if name, err = b.RealPath(name); err != nil {
    152 		return nil, &os.PathError{Op: "open", Path: name, Err: err}
    153 	}
    154 	sourcef, err := b.source.Open(name)
    155 	if err != nil {
    156 		return nil, err
    157 	}
    158 	return &BasePathFile{File: sourcef, path: b.path}, nil
    159 }
    160 
    161 func (b *BasePathFs) Mkdir(name string, mode os.FileMode) (err error) {
    162 	if name, err = b.RealPath(name); err != nil {
    163 		return &os.PathError{Op: "mkdir", Path: name, Err: err}
    164 	}
    165 	return b.source.Mkdir(name, mode)
    166 }
    167 
    168 func (b *BasePathFs) MkdirAll(name string, mode os.FileMode) (err error) {
    169 	if name, err = b.RealPath(name); err != nil {
    170 		return &os.PathError{Op: "mkdir", Path: name, Err: err}
    171 	}
    172 	return b.source.MkdirAll(name, mode)
    173 }
    174 
    175 func (b *BasePathFs) Create(name string) (f File, err error) {
    176 	if name, err = b.RealPath(name); err != nil {
    177 		return nil, &os.PathError{Op: "create", Path: name, Err: err}
    178 	}
    179 	sourcef, err := b.source.Create(name)
    180 	if err != nil {
    181 		return nil, err
    182 	}
    183 	return &BasePathFile{File: sourcef, path: b.path}, nil
    184 }
    185 
    186 func (b *BasePathFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
    187 	name, err := b.RealPath(name)
    188 	if err != nil {
    189 		return nil, false, &os.PathError{Op: "lstat", Path: name, Err: err}
    190 	}
    191 	if lstater, ok := b.source.(Lstater); ok {
    192 		return lstater.LstatIfPossible(name)
    193 	}
    194 	fi, err := b.source.Stat(name)
    195 	return fi, false, err
    196 }
    197 
    198 func (b *BasePathFs) SymlinkIfPossible(oldname, newname string) error {
    199 	oldname, err := b.RealPath(oldname)
    200 	if err != nil {
    201 		return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: err}
    202 	}
    203 	newname, err = b.RealPath(newname)
    204 	if err != nil {
    205 		return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: err}
    206 	}
    207 	if linker, ok := b.source.(Linker); ok {
    208 		return linker.SymlinkIfPossible(oldname, newname)
    209 	}
    210 	return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink}
    211 }
    212 
    213 func (b *BasePathFs) ReadlinkIfPossible(name string) (string, error) {
    214 	name, err := b.RealPath(name)
    215 	if err != nil {
    216 		return "", &os.PathError{Op: "readlink", Path: name, Err: err}
    217 	}
    218 	if reader, ok := b.source.(LinkReader); ok {
    219 		return reader.ReadlinkIfPossible(name)
    220 	}
    221 	return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink}
    222 }