Skip to content
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

Merged
merged 5 commits into from
Jun 11, 2018

Conversation

jfontan
Copy link
Contributor

@jfontan jfontan commented May 16, 2018

This adds the Capable interface that can be implemented in filesystems
to return the features suported by it. This first iteration has the
following capablities:

  • CapWrite
  • CapRead
  • CapReadAndWrite
  • CapSeek
  • CapTruncate
  • CapLock

billy.Capablilities(fs) can be used to query a fs. The capabilities
are implemented as bit flags. If a filesystem does not implement Capable
interface then all capabilities will be returned as fall back.

This adds the Capable interface that can be implemented in filesystems
to return the features suported by it. This first iteration has the
following capablities:

* CapWrite
* CapRead
* CapReadAndWrite
* CapSeek
* CapTruncate
* CapLock

`billy.Capablilities(fs)` can be used to query a fs. The capabilities
are implemented as bit flags. If a filesystem does not implement Capable
interface then all capabilities will be returned as fall back.

Signed-off-by: Javi Fontan <jfontan@gmail.com>
@jfontan jfontan requested review from smola, ajnavarro and mcuadros May 16, 2018 09:26
Signed-off-by: Javi Fontan <jfontan@gmail.com>
Copy link
Contributor

@ajnavarro ajnavarro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smola @mcuadros I like a lot the idea, and with this, we can remove the special compile flag that we have in go-git to be able to use siva filesystem.

But anyways, maybe we need to do a second pass to define capabilities.


// 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The 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 Capable, not something that can evolve over time.

Maybe we could rename CapAll to CapDefault and state explicitly in the documentation that CapDefault values will never change without a major version bump.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My comment is silly and very personal :) but just adding 2 cents...
For me Cap sounds like Capacity (especially that we have in go global function cap which returns capacity). So maybe we can rename Capability to Ability (or FSMode) and consts to Readable, Writable, Seekable, Lockable, ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 (O_RDONLY | O_SYNC) for operations that are not implemented in the billy FS even if the methods are there. One example is go-billy-siva. The files can not be opened in read and write mode. We can check with this flags before using one algorithm or the other:

if billy.CapabilityCheck(mySivaFS, billy.ReadAndWrite) {
....
}

Other example is memory FS. The locking methods are no-ops.

Capability may be changed to Feature if it seems too close to capacity.

Copy link

Choose a reason for hiding this comment

The 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 cap looks more like capacity.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Capability prefix instead of Cap also looks more in line with our convention of avoiding abbreviations, specially if there is any ambiguity.

@@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why fs.underlying and not fs.source? If Capabilities are global to the filesystem, this should probably return fs.underlying.Capabilities()&fs.source.Capabilities(), which is the common denominator, even if there is some directory with additional capabilities.

@@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, adding some checking for osfs would be awesome. And probably it would still be no guarantee, since we might have a nasty fuse filesystem that is mounted as read-write but actually be pure read-only.

fs.go Outdated
@@ -13,6 +13,28 @@ var (
ErrCrossedBoundary = errors.New("chroot boundary crossed")
)

// Capability holds the supported features of a filesystem.
type Capability uint64
Copy link
Contributor

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:

  • Lots of filesystems, including osfs depending on the underlying filesystem, will have additional restrictions depending on the actual filesystem implementation, operating system, mount options, etc.
  • In some cases, even if it is theoretically possible to know that a filesystem is fully read-only (e.g. 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 returning nil.

CapDefault has all the capabilities supported in the current release
and should not be changed until a new major release.

Also changed Capabilities of mount to return the common capabilities
from both filesystems.

Signed-off-by: Javi Fontan <jfontan@gmail.com>
@jfontan
Copy link
Contributor Author

jfontan commented Jun 4, 2018

@smola I believe I have addressed your concerns in the latest commit.

@ajnavarro The capabilities added are what I could get from the top of my head and the use cases I have. I agree that more people should take a look and refine them.

fs.go Outdated

const (
// CapWrite means that the fs is writable.
CapWrite Capability = 1 << iota
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change the names to WriteCapability and so on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

fs.go Outdated
// CapDefault lists all capable features supported by filesystems without
// Capability interface. This list should not be changed until a major
// version is released.
CapDefault Capability = CapWrite | CapRead | CapReadAndWrite |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, DefaultCapabilities

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Signed-off-by: Javi Fontan <jfontan@gmail.com>
@smola
Copy link
Contributor

smola commented Jun 6, 2018

@jfontan Could you add some more tests? https://codecov.io/gh/src-d/go-billy/compare/df053870ae7070b0350624ba5a22161ba3796cc0...5bb98d7916f7d944463d78e61e9f5b59dd166360/diff

  • Check capabilities on polyfill, mount and chroot.
  • Check that Capabilities top-level function falls back to DefaultCapabilities for a filesystem that does not implement it.

Also use mock FS instead of memfs to test capabilities

Signed-off-by: Javi Fontan <jfontan@gmail.com>
@jfontan
Copy link
Contributor Author

jfontan commented Jun 8, 2018

@smola done

@mcuadros mcuadros merged commit 83cf655 into src-d:master Jun 11, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants