-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Capability function to query fs capabilities #59
Changes from 2 commits
a22d183
d198577
e2ca534
5bb98d7
c1e3d52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,28 @@ var ( | |
ErrCrossedBoundary = errors.New("chroot boundary crossed") | ||
) | ||
|
||
// Capability holds the supported features of a filesystem. | ||
type Capability uint64 | ||
|
||
const ( | ||
// CapWrite means that the fs is writable. | ||
CapWrite Capability = 1 << iota | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would change the names to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
// CapRead means that the fs is readable. | ||
CapRead | ||
// CapReadAndWrite is the ability to open a file in read and write mode. | ||
CapReadAndWrite | ||
// CapSeek means it is able to move position inside the file. | ||
CapSeek | ||
// CapTruncate means that a file can be truncated. | ||
CapTruncate | ||
// CapLock is the ability to lock a file. | ||
CapLock | ||
|
||
// CapAll lists all capable features. | ||
CapAll Capability = CapWrite | CapRead | CapReadAndWrite | | ||
CapSeek | CapTruncate | CapLock | ||
) | ||
|
||
// Filesystem abstract the operations in a storage-agnostic interface. | ||
// Each method implementation mimics the behavior of the equivalent functions | ||
// at the os package from the standard library. | ||
|
@@ -143,3 +165,27 @@ type File interface { | |
// Truncate the file. | ||
Truncate(size int64) error | ||
} | ||
|
||
// Capable interface can return the available features of a filesystem. | ||
type Capable interface { | ||
// Capabilities returns the capabilities of a filesystem in bit flags. | ||
Capabilities() Capability | ||
} | ||
|
||
// Capabilities returns the features supported by a filesystem. If the FS | ||
// does not implement Capable interface it returns all features. | ||
func Capabilities(fs Basic) Capability { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In order to maintain backwards compatibility, this method should assume a fixed set of capabilities for filesystems not implementing Maybe we could rename There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My comment is silly and very personal :) but just adding 2 cents... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is something like this for the interfaces implemented by the FS. This implementation may not be very Goish. It tries to mimic open flags (
Other example is memory FS. The locking methods are no-ops.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feature sounds good to me. But I'm not totally against capability, but just abbreviation There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using |
||
capable, ok := fs.(Capable) | ||
if !ok { | ||
return CapAll | ||
} | ||
|
||
return capable.Capabilities() | ||
} | ||
|
||
// CapabilityCheck tests the filesystem for the provided capabilities and | ||
// returns true in case it supports all of them. | ||
func CapabilityCheck(fs Basic, capabilities Capability) bool { | ||
fsCaps := Capabilities(fs) | ||
return fsCaps&capabilities == capabilities | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package billy_test | ||
|
||
import ( | ||
"testing" | ||
|
||
. "gopkg.in/src-d/go-billy.v4" | ||
"gopkg.in/src-d/go-billy.v4/memfs" | ||
|
||
. "gopkg.in/check.v1" | ||
) | ||
|
||
type FSSuite struct{} | ||
|
||
func Test(t *testing.T) { TestingT(t) } | ||
|
||
var _ = Suite(&FSSuite{}) | ||
|
||
func (s *FSSuite) TestCapabilities(c *C) { | ||
cases := []struct { | ||
caps Capability | ||
expected bool | ||
}{ | ||
{CapLock, false}, | ||
{CapRead, true}, | ||
{CapRead | CapWrite, true}, | ||
{CapRead | CapWrite | CapReadAndWrite | CapTruncate, true}, | ||
{CapRead | CapWrite | CapReadAndWrite | CapTruncate | CapLock, false}, | ||
{CapTruncate | CapLock, false}, | ||
} | ||
|
||
// This filesystem supports all capabilities except for CapLock | ||
mem := memfs.New() | ||
|
||
for _, e := range cases { | ||
c.Assert(CapabilityCheck(mem, e.caps), Equals, e.expected) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -167,6 +167,11 @@ func (h *Mount) Underlying() billy.Basic { | |
return h.underlying | ||
} | ||
|
||
// Capabilities implements the Capable interface. | ||
func (fs *Mount) Capabilities() billy.Capability { | ||
return billy.Capabilities(fs.underlying) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why |
||
} | ||
|
||
func (fs *Mount) getBasicAndPath(path string) (billy.Basic, string) { | ||
path = cleanPath(path) | ||
if !fs.isMountpoint(path) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -127,6 +127,11 @@ func (fs *OS) Readlink(link string) (string, error) { | |
return os.Readlink(link) | ||
} | ||
|
||
// Capabilities implements the Capable interface. | ||
func (fs *OS) Capabilities() billy.Capability { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Documentation of this should state that actual capabilities are dependent on the underlying filesystem. So, for example, if the filesystem is mounted as read-only, it cannot be written and it will return error on opening in write mode. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice that each filesystem could query these, that is, detect that the FS is mounted read only and do not return CapWrite. Either way the Capability was more about what was implemented in the billy filesystem than in the underlying OS fs. 👍 to explicitly tell this in the documentation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, adding some checking for |
||
return billy.CapAll | ||
} | ||
|
||
// file is a wrapper for an os.File which adds support for file locking. | ||
type file struct { | ||
*os.File | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation should be more clear about what a capability means. A capability, defined as filesystem-wide, can only be a hint and not a guarantee, because:
osfs
depending on the underlying filesystem, will have additional restrictions depending on the actual filesystem implementation, operating system, mount options, etc.os
filesystem with read-only mount), we just do not check it.Users should always be prepared to handle gracefully any possible error and do not assume that a capability makes any function returning an
error
always returningnil
.