Skip to content

Commit

Permalink
Merge pull request #35 from mcuadros/polyfill
Browse files Browse the repository at this point in the history
helper: new polyfill helper, and mount and chroot simplification
  • Loading branch information
mcuadros committed Jun 16, 2017
2 parents bfa45f0 + 672c9bf commit d48e2a7
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 70 deletions.
2 changes: 1 addition & 1 deletion fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ type Chroot interface {
// Chroot returns a new filesystem from the same type where the new root is
// the given path. Files outside of the designated directory tree cannot be
// accessed.
Chroot(path string) (Basic, error)
Chroot(path string) (Filesystem, error)
// Root returns the root path of the filesystem.
Root() string
}
Expand Down
43 changes: 7 additions & 36 deletions helper/chroot/chroot.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,23 @@ import (
"strings"

"gopkg.in/src-d/go-billy.v3"
"gopkg.in/src-d/go-billy.v3/helper/polyfill"
)

// ChrootHelper is a helper to implement billy.Chroot.
type ChrootHelper struct {
underlying billy.Basic
underlying billy.Filesystem
base string

dirSupport bool
symlinkSupport bool
tempFileSupport bool
}

// New creates a new filesystem wrapping up the given 'fs'.
// The created filesystem has its base in the given ChrootHelperectory of the
// underlying filesystem.
func New(fs billy.Basic, base string) billy.Filesystem {
helper := &ChrootHelper{underlying: fs, base: base}
_, helper.dirSupport = fs.(billy.Dir)
_, helper.symlinkSupport = fs.(billy.Symlink)
_, helper.tempFileSupport = fs.(billy.TempFile)

return helper
return &ChrootHelper{
underlying: polyfill.New(fs),
base: base,
}
}

func (fs *ChrootHelper) underlyingPath(filename string) (string, error) {
Expand Down Expand Up @@ -125,10 +120,6 @@ func (fs *ChrootHelper) Join(elem ...string) string {
}

func (fs *ChrootHelper) TempFile(dir, prefix string) (billy.File, error) {
if !fs.tempFileSupport {
return nil, billy.ErrNotSupported
}

fullpath, err := fs.underlyingPath(dir)
if err != nil {
return nil, err
Expand All @@ -143,10 +134,6 @@ func (fs *ChrootHelper) TempFile(dir, prefix string) (billy.File, error) {
}

func (fs *ChrootHelper) ReadDir(path string) ([]os.FileInfo, error) {
if !fs.dirSupport {
return nil, billy.ErrNotSupported
}

fullpath, err := fs.underlyingPath(path)
if err != nil {
return nil, err
Expand All @@ -156,10 +143,6 @@ func (fs *ChrootHelper) ReadDir(path string) ([]os.FileInfo, error) {
}

func (fs *ChrootHelper) MkdirAll(filename string, perm os.FileMode) error {
if !fs.dirSupport {
return billy.ErrNotSupported
}

fullpath, err := fs.underlyingPath(filename)
if err != nil {
return err
Expand All @@ -169,10 +152,6 @@ func (fs *ChrootHelper) MkdirAll(filename string, perm os.FileMode) error {
}

func (fs *ChrootHelper) Lstat(filename string) (os.FileInfo, error) {
if !fs.symlinkSupport {
return nil, billy.ErrNotSupported
}

fullpath, err := fs.underlyingPath(filename)
if err != nil {
return nil, err
Expand All @@ -182,10 +161,6 @@ func (fs *ChrootHelper) Lstat(filename string) (os.FileInfo, error) {
}

func (fs *ChrootHelper) Symlink(target, link string) error {
if !fs.symlinkSupport {
return billy.ErrNotSupported
}

target = filepath.FromSlash(target)

// only rewrite target if it's already absolute
Expand Down Expand Up @@ -218,10 +193,6 @@ func (fs *ChrootHelper) isTargetOutBounders(link, target string) bool {
}

func (fs *ChrootHelper) Readlink(link string) (string, error) {
if !fs.symlinkSupport {
return "", billy.ErrNotSupported
}

fullpath, err := fs.underlyingPath(link)
if err != nil {
return "", err
Expand All @@ -244,7 +215,7 @@ func (fs *ChrootHelper) Readlink(link string) (string, error) {
return string(os.PathSeparator) + target, nil
}

func (fs *ChrootHelper) Chroot(path string) (billy.Basic, error) {
func (fs *ChrootHelper) Chroot(path string) (billy.Filesystem, error) {
fullpath, err := fs.underlyingPath(path)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion helper/chroot/chroot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func (s *ChrootSuite) TestSymlinkWithBasic(c *C) {
m := &test.BasicMock{}

fs := New(m, "/foo")
err := fs.Symlink("", "")
err := fs.Symlink("qux", "bar")
c.Assert(err, Equals, billy.ErrNotSupported)
}

Expand Down
42 changes: 10 additions & 32 deletions helper/mount/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"

"gopkg.in/src-d/go-billy.v3"
"gopkg.in/src-d/go-billy.v3/helper/polyfill"
)

var separator = string(filepath.Separator)
Expand All @@ -17,30 +18,19 @@ var separator = string(filepath.Separator)
// Very usufull to create a temporal dir, on filesystem where is a performance
// penalty in doing so.
type Mount struct {
underlying billy.Basic
source billy.Basic
underlying billy.Filesystem
source billy.Filesystem
mountpoint string

uc capabilities // underlying
sc capabilities // source
}

type capabilities struct{ dir, symlink bool }

// New creates a new filesystem wrapping up 'fs' the intercepts all the calls
// made to `mountpoint` path and redirecting it to `source` filesystem.
func New(fs billy.Basic, mountpoint string, source billy.Basic) *Mount {
h := &Mount{
underlying: fs,
source: source,
return &Mount{
underlying: polyfill.New(fs),
source: polyfill.New(source),
mountpoint: cleanPath(mountpoint),
}

_, h.sc.dir = h.source.(billy.Dir)
_, h.sc.symlink = h.source.(billy.Symlink)
_, h.uc.dir = h.underlying.(billy.Dir)
_, h.uc.symlink = h.underlying.(billy.Symlink)
return h
}

func (h *Mount) Create(path string) (billy.File, error) {
Expand Down Expand Up @@ -169,6 +159,10 @@ func (h *Mount) Lstat(path string) (os.FileInfo, error) {
return fs.Lstat(fullpath)
}

func (h *Mount) Underlying() billy.Basic {
return h.underlying
}

func (fs *Mount) getBasicAndPath(path string) (billy.Basic, string) {
path = cleanPath(path)
if !fs.isMountpoint(path) {
Expand All @@ -181,34 +175,18 @@ func (fs *Mount) getBasicAndPath(path string) (billy.Basic, string) {
func (fs *Mount) getDirAndPath(path string) (billy.Dir, string, error) {
path = cleanPath(path)
if !fs.isMountpoint(path) {
if !fs.uc.dir {
return nil, "", billy.ErrNotSupported
}

return fs.underlying.(billy.Dir), path, nil
}

if !fs.sc.dir {
return nil, "", billy.ErrNotSupported
}

return fs.source.(billy.Dir), fs.mustRelToMountpoint(path), nil
}

func (fs *Mount) getSymlinkAndPath(path string) (billy.Symlink, string, error) {
path = cleanPath(path)
if !fs.isMountpoint(path) {
if !fs.uc.symlink {
return nil, "", billy.ErrNotSupported
}

return fs.underlying.(billy.Symlink), path, nil
}

if !fs.sc.symlink {
return nil, "", billy.ErrNotSupported
}

return fs.source.(billy.Symlink), fs.mustRelToMountpoint(path), nil
}

Expand Down
100 changes: 100 additions & 0 deletions helper/polyfill/polyfill.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package polyfill

import (
"os"
"path/filepath"

"gopkg.in/src-d/go-billy.v3"
)

// Polyfill is a helper that implements all missing method from billy.Filesystem.
type Polyfill struct {
billy.Basic
c capabilities
}

type capabilities struct{ tempfile, dir, symlink, chroot bool }

// New creates a new filesystem wrapping up 'fs' the intercepts all the calls
// made and errors if fs doesn't implement any of the billy interfaces.
func New(fs billy.Basic) billy.Filesystem {
if original, ok := fs.(billy.Filesystem); ok {
return original
}

h := &Polyfill{Basic: fs}

_, h.c.tempfile = h.Basic.(billy.TempFile)
_, h.c.dir = h.Basic.(billy.Dir)
_, h.c.symlink = h.Basic.(billy.Symlink)
_, h.c.chroot = h.Basic.(billy.Chroot)
return h
}

func (h *Polyfill) TempFile(dir, prefix string) (billy.File, error) {
if !h.c.tempfile {
return nil, billy.ErrNotSupported
}

return h.Basic.(billy.TempFile).TempFile(dir, prefix)
}

func (h *Polyfill) ReadDir(path string) ([]os.FileInfo, error) {
if !h.c.dir {
return nil, billy.ErrNotSupported
}

return h.Basic.(billy.Dir).ReadDir(path)
}

func (h *Polyfill) MkdirAll(filename string, perm os.FileMode) error {
if !h.c.dir {
return billy.ErrNotSupported
}

return h.Basic.(billy.Dir).MkdirAll(filename, perm)
}

func (h *Polyfill) Symlink(target, link string) error {
if !h.c.symlink {
return billy.ErrNotSupported
}

return h.Basic.(billy.Symlink).Symlink(target, link)
}

func (h *Polyfill) Readlink(link string) (string, error) {
if !h.c.symlink {
return "", billy.ErrNotSupported
}

return h.Basic.(billy.Symlink).Readlink(link)
}

func (h *Polyfill) Lstat(path string) (os.FileInfo, error) {
if !h.c.symlink {
return nil, billy.ErrNotSupported
}

return h.Basic.(billy.Symlink).Lstat(path)
}

func (h *Polyfill) Chroot(path string) (billy.Filesystem, error) {
if !h.c.chroot {
return nil, billy.ErrNotSupported
}

return h.Basic.(billy.Chroot).Chroot(path)
}

func (h *Polyfill) Root() string {
if !h.c.chroot {
return string(filepath.Separator)
}

return h.Basic.(billy.Chroot).Root()
}

func (h *Polyfill) Underlying() billy.Basic {
return h.Basic
}
63 changes: 63 additions & 0 deletions helper/polyfill/polyfill_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package polyfill

import (
"path/filepath"
"testing"

"gopkg.in/src-d/go-billy.v3"
"gopkg.in/src-d/go-billy.v3/test"

. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

var _ = Suite(&PolyfillSuite{})

type PolyfillSuite struct {
Helper billy.Filesystem
Underlying billy.Filesystem
}

func (s *PolyfillSuite) SetUpTest(c *C) {
s.Helper = New(&test.BasicMock{})
}

func (s *PolyfillSuite) TestTempFile(c *C) {
_, err := s.Helper.TempFile("", "")
c.Assert(err, Equals, billy.ErrNotSupported)
}

func (s *PolyfillSuite) TestReadDir(c *C) {
_, err := s.Helper.ReadDir("")
c.Assert(err, Equals, billy.ErrNotSupported)
}

func (s *PolyfillSuite) TestMkdirAll(c *C) {
err := s.Helper.MkdirAll("", 0)
c.Assert(err, Equals, billy.ErrNotSupported)
}

func (s *PolyfillSuite) TestSymlink(c *C) {
err := s.Helper.Symlink("", "")
c.Assert(err, Equals, billy.ErrNotSupported)
}

func (s *PolyfillSuite) TestReadlink(c *C) {
_, err := s.Helper.Readlink("")
c.Assert(err, Equals, billy.ErrNotSupported)
}

func (s *PolyfillSuite) TestLstat(c *C) {
_, err := s.Helper.Lstat("")
c.Assert(err, Equals, billy.ErrNotSupported)
}

func (s *PolyfillSuite) TestChroot(c *C) {
_, err := s.Helper.Chroot("")
c.Assert(err, Equals, billy.ErrNotSupported)
}

func (s *PolyfillSuite) TestRoot(c *C) {
c.Assert(s.Helper.Root(), Equals, string(filepath.Separator))
}

0 comments on commit d48e2a7

Please sign in to comment.