-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create the new BoundOS osfs type, which works as a pass-through filesystem for files descending from base dir. For backwards compatibility the previous behaviour is still the default and is now represented by the ChrootOS type. Signed-off-by: Paulo Gomes <[email protected]>
- Loading branch information
Showing
15 changed files
with
2,235 additions
and
139 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,25 @@ | ||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= | ||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= | ||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= | ||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= | ||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= | ||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= | ||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= | ||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= | ||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= | ||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= | ||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
//go:build !js | ||
// +build !js | ||
|
||
// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. | ||
// Copyright (C) 2017 SUSE LLC. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// Adapted from upstream: | ||
// https://github.com/cyphar/filepath-securejoin/blob/64536a8a66ae59588c981e2199f1dcf410508e07/join.go | ||
|
||
package util | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"syscall" | ||
) | ||
|
||
// IsNotExist tells you if err is an error that implies that either the path | ||
// accessed does not exist (or path components don't exist). This is | ||
// effectively a more broad version of os.IsNotExist. | ||
func IsNotExist(err error) bool { | ||
// Check that it's not actually an ENOTDIR, which in some cases is a more | ||
// convoluted case of ENOENT (usually involving weird paths). | ||
return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT) | ||
} | ||
|
||
// SecureJoinVFS joins the two given path components (similar to Join) except | ||
// that the returned path is guaranteed to be scoped inside the provided root | ||
// path (when evaluated). Any symbolic links in the path are evaluated with the | ||
// given root treated as the root of the filesystem, similar to a chroot. The | ||
// filesystem state is evaluated through the given VFS interface (if nil, the | ||
// standard os.* family of functions are used). | ||
// | ||
// Note that the guarantees provided by this function only apply if the path | ||
// components in the returned string are not modified (in other words are not | ||
// replaced with symlinks on the filesystem) after this function has returned. | ||
// Such a symlink race is necessarily out-of-scope of SecureJoin. | ||
func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) { | ||
// Use the os.* VFS implementation if none was specified. | ||
if vfs == nil { | ||
vfs = osVFS{} | ||
} | ||
|
||
var path bytes.Buffer | ||
n := 0 | ||
for unsafePath != "" { | ||
if n > 255 { | ||
return "", &os.PathError{Op: "SecureJoin", Path: root + string(filepath.Separator) + unsafePath, Err: syscall.ELOOP} | ||
} | ||
|
||
// Change upstream's behaviour: | ||
// | ||
// The volume name must be stripped to avoid invalid paths. | ||
// Only the root path should contain the volume name. | ||
if v := filepath.VolumeName(unsafePath); v != "" { | ||
unsafePath = unsafePath[len(v):] | ||
} | ||
|
||
// Next path component, p. | ||
i := strings.IndexRune(unsafePath, filepath.Separator) | ||
var p string | ||
if i == -1 { | ||
p, unsafePath = unsafePath, "" | ||
} else { | ||
p, unsafePath = unsafePath[:i], unsafePath[i+1:] | ||
} | ||
|
||
// Create a cleaned path, using the lexical semantics of /../a, to | ||
// create a "scoped" path component which can safely be joined to fullP | ||
// for evaluation. At this point, path.String() doesn't contain any | ||
// symlink components. | ||
cleanP := filepath.Clean(string(filepath.Separator) + path.String() + p) | ||
if cleanP == string(filepath.Separator) { | ||
path.Reset() | ||
continue | ||
} | ||
fullP := filepath.Clean(root + cleanP) | ||
|
||
// Figure out whether the path is a symlink. | ||
fi, err := vfs.Lstat(fullP) | ||
if err != nil && !IsNotExist(err) { | ||
return "", err | ||
} | ||
// Treat non-existent path components the same as non-symlinks (we | ||
// can't do any better here). | ||
if IsNotExist(err) || fi.Mode()&os.ModeSymlink == 0 { | ||
path.WriteString(p) | ||
path.WriteRune(filepath.Separator) | ||
continue | ||
} | ||
|
||
// Only increment when we actually dereference a link. | ||
n++ | ||
|
||
// It's a symlink, expand it by prepending it to the yet-unparsed path. | ||
dest, err := vfs.Readlink(fullP) | ||
if err != nil { | ||
return "", err | ||
} | ||
// Absolute symlinks reset any work we've already done. | ||
if filepath.IsAbs(dest) { | ||
// Change upstream's behaviour: | ||
// | ||
// Avoid duplicating root dir due to abs symlinks. | ||
dest = strings.Replace(dest, root+string(filepath.Separator), string(filepath.Separator), 1) | ||
path.Reset() | ||
} | ||
unsafePath = dest + string(filepath.Separator) + unsafePath | ||
} | ||
|
||
// We have to clean path.String() here because it may contain '..' | ||
// components that are entirely lexical, but would be misleading otherwise. | ||
// And finally do a final clean to ensure that root is also lexically | ||
// clean. | ||
fullP := filepath.Clean(string(filepath.Separator) + path.String()) | ||
return filepath.Clean(root + fullP), nil | ||
} | ||
|
||
// SecureJoin is a wrapper around SecureJoinVFS that just uses the os.* library | ||
// of functions as the VFS. If in doubt, use this function over SecureJoinVFS. | ||
func SecureJoin(root, unsafePath string) (string, error) { | ||
return SecureJoinVFS(root, unsafePath, nil) | ||
} | ||
|
||
// In future this should be moved into a separate package, because now there | ||
// are several projects (umoci and go-mtree) that are using this sort of | ||
// interface. | ||
|
||
// VFS is the minimal interface necessary to use SecureJoinVFS. A nil VFS is | ||
// equivalent to using the standard os.* family of functions. This is mainly | ||
// used for the purposes of mock testing, but also can be used to otherwise use | ||
// SecureJoin with VFS-like system. | ||
type VFS interface { | ||
// Lstat returns a FileInfo describing the named file. If the file is a | ||
// symbolic link, the returned FileInfo describes the symbolic link. Lstat | ||
// makes no attempt to follow the link. These semantics are identical to | ||
// os.Lstat. | ||
Lstat(name string) (os.FileInfo, error) | ||
|
||
// Readlink returns the destination of the named symbolic link. These | ||
// semantics are identical to os.Readlink. | ||
Readlink(name string) (string, error) | ||
} | ||
|
||
// osVFS is the "nil" VFS, in that it just passes everything through to the os | ||
// module. | ||
type osVFS struct{} | ||
|
||
// Lstat returns a FileInfo describing the named file. If the file is a | ||
// symbolic link, the returned FileInfo describes the symbolic link. Lstat | ||
// makes no attempt to follow the link. These semantics are identical to | ||
// os.Lstat. | ||
func (o osVFS) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) } | ||
|
||
// Readlink returns the destination of the named symbolic link. These | ||
// semantics are identical to os.Readlink. | ||
func (o osVFS) Readlink(name string) (string, error) { return os.Readlink(name) } |
Oops, something went wrong.