Add BasePathFs as FilterFs

* add a BasePathFs implemented as FilterFs:
  The BasePathFs restricts all operations to a given path within an Fs.
  The given file name to the operations on this Fs will be prepended
  with the base path before calling the base Fs.
  Any file name (after filepath.Clean()) outside this base path will be
  treated as non existing file.

* fix meaning of "facere" - "facio" means "I do", "facere" is the base
  form
This commit is contained in:
Hanno Hecker 2015-12-30 08:16:22 +01:00 committed by Steve Francia
parent 49e01f227a
commit 205066d391
3 changed files with 161 additions and 5 deletions

View File

@ -278,7 +278,6 @@ implement:
* TAR * TAR
* S3 * S3
* Mem buffering to disk/network * Mem buffering to disk/network
* BasePath (where all paths are relative to a fixed basepath)
# Filters # Filters
@ -299,12 +298,20 @@ The `AddFilter` adds a new FilterFs before any existing filters.
## Available filters ## Available filters
* NewReadonlyFilter() - provide a read only view of the source Fs # NewBasePathFs(Fs, path)
* NewRegexpFilter(*regexp.Regexp) - provide a filtered view on file names, any
file (not directory) NOT matching the passed regexp will be treated as
non-existing
The BasePathFs restricts all operations to a given path within an Fs.
The given file name to the operations on this Fs will be prepended with
the base path before calling the base Fs.
# NewReadonlyFilter()
provide a read only view of the source Fs
# NewRegexpFilter(\*regexp.Regexp)
provide a filtered view on file names, any file (not directory) NOT matching
the passed regexp will be treated as non-existing
# About the project # About the project

130
basepath.go Normal file
View File

@ -0,0 +1,130 @@
package afero
import (
"os"
"path/filepath"
"strings"
"time"
)
type BasePathFs struct {
base Fs
path string
}
// The BasePathFs restricts all operations to a given path within an Fs.
// The given file name to the operations on this Fs will be prepended with
// the base path before calling the base Fs.
// Any file name (after filepath.Clean()) outside this base path will be
// treated as non existing file.
//
// Note that it does not clean the error messages on return, so you may
// reveal the real path on errors.
func NewBasePathFs(base Fs, path string) FilterFs {
return &BasePathFs{base: base, path: filepath.Clean(path) + string(os.PathSeparator)}
}
func (b *BasePathFs) AddFilter(fs FilterFs) {
fs.SetSource(b.base)
b.base = fs
}
func (b *BasePathFs) SetSource(fs Fs) {
b.base = fs
}
// on a file outside the base path it returns the given file name and an error,
// else the given file with the base path prepended
func (b *BasePathFs) RealPath(name string) (path string, err error) {
path = filepath.Clean(filepath.Join(b.path, name))
if !strings.HasPrefix(path, b.path) {
return name, os.ErrNotExist
}
return path, nil
}
func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) {
if name, err = b.RealPath(name); err != nil {
return &os.PathError{"chtimes", name, err}
}
return b.base.Chtimes(name, atime, mtime)
}
func (b *BasePathFs) Chmod(name string, mode os.FileMode) (err error) {
if name, err = b.RealPath(name); err != nil {
return &os.PathError{"chmod", name, err}
}
return b.base.Chmod(name, mode)
}
func (b *BasePathFs) Name() string {
return "BasePathFs"
}
func (b *BasePathFs) Stat(name string) (fi os.FileInfo, err error) {
if name, err = b.RealPath(name); err != nil {
return nil, &os.PathError{"stat", name, err}
}
return b.base.Stat(name)
}
func (b *BasePathFs) Rename(oldname, newname string) (err error) {
if oldname, err = b.RealPath(oldname); err != nil {
return &os.PathError{"rename", oldname, err}
}
if newname, err = b.RealPath(newname); err != nil {
return &os.PathError{"rename", newname, err}
}
return b.base.Rename(oldname, newname)
}
func (b *BasePathFs) RemoveAll(name string) (err error) {
if name, err = b.RealPath(name); err != nil {
return &os.PathError{"remove_all", name, err}
}
return b.base.RemoveAll(name)
}
func (b *BasePathFs) Remove(name string) (err error) {
if name, err = b.RealPath(name); err != nil {
return &os.PathError{"remove", name, err}
}
return b.base.Remove(name)
}
func (b *BasePathFs) OpenFile(name string, flag int, mode os.FileMode) (f File, err error) {
if name, err = b.RealPath(name); err != nil {
return nil, &os.PathError{"openfile", name, err}
}
return b.base.OpenFile(name, flag, mode)
}
func (b *BasePathFs) Open(name string) (f File, err error) {
if name, err = b.RealPath(name); err != nil {
return nil, &os.PathError{"open", name, err}
}
return b.base.Open(name)
}
func (b *BasePathFs) Mkdir(name string, mode os.FileMode) (err error) {
if name, err = b.RealPath(name); err != nil {
return &os.PathError{"mkdir", name, err}
}
return b.base.Mkdir(name, mode)
}
func (b *BasePathFs) MkdirAll(name string, mode os.FileMode) (err error) {
if name, err = b.RealPath(name); err != nil {
return &os.PathError{"mkdir", name, err}
}
return b.base.MkdirAll(name, mode)
}
func (b *BasePathFs) Create(name string) (f File, err error) {
if name, err = b.RealPath(name); err != nil {
return nil, &os.PathError{"create", name, err}
}
return b.base.Create(name)
}
// vim: ts=4 sw=4 noexpandtab nolist syn=go

19
basepath_test.go Normal file
View File

@ -0,0 +1,19 @@
package afero
import (
"testing"
)
func TestBasePath(t *testing.T) {
baseFs := &MemMapFs{}
baseFs.MkdirAll("/base/path/tmp", 0777)
bp := NewBasePathFs(baseFs, "/base/path")
if _, err := bp.Create("/tmp/foo"); err != nil {
t.Errorf("Failed to set real path")
}
if fh, err := bp.Create("../tmp/bar"); err == nil {
t.Errorf("succeeded in creating %s ...", fh.Name())
}
}