github.com/xtrafrancyz/afero@v1.1.1/basepath.go (about)

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