basepath.go - afero - [fork] go afero port for 9front
 (HTM) git clone https://git.drkhsh.at/afero.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) 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 }