Skip to content
This repository has been archived by the owner on Jun 19, 2023. It is now read-only.

[RFC] MFS interface draft #19

Closed
wants to merge 11 commits into from
Closed
2 changes: 2 additions & 0 deletions coreapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type CoreAPI interface {
// ObjectAPI returns an implementation of Object API
Object() ObjectAPI

Files() MfsAPI

// Dht returns an implementation of Dht API
Dht() DhtAPI

Expand Down
84 changes: 84 additions & 0 deletions mfs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package iface

import (
"context"
"io"
"os"
"strings"
)

// MfsPath
//
// Note that Path and ResolvedPath interfaces satisfy this interface
//
// This interface is inspired by https://godoc.org/github.com/src-d/go-billy,
// but doesn't implement it
type MfsPath interface {
Copy link
Member

Choose a reason for hiding this comment

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

We don't have to do this now, but I'd like to prefix all MFS paths with /local. If we do that, we won't have to have this special type (and we'll get rid of a bunch of namespace conflict issues.

(the upgrade path will be interesting but we may have to save this for an API refactor)

Copy link
Member Author

Choose a reason for hiding this comment

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

  • Should we support resolving /local outside Files API?
    • Yes but disable on gateway api?
  • How should ipfs files commands handle those?
    • Just prefix everything with /local?
      • Might be confusing for users (i.e. why those commands have to be different)
      • Does /ipfs equal /local/ipfs everywhere?

I slowly starting to think that using fancy types for paths isn't the best way to handle them (#16, #15), but using raw strings introduces another set of problems (CID serialization to name one). There is some trade-off to be made here..

Copy link

Choose a reason for hiding this comment

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

FWIW, in the mount implementation I have the Files API mounted under /files, I almost feel like /local is a bit of a misnomer given that files placed there are still published/accessible to the network.

Copy link
Member Author

Choose a reason for hiding this comment

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

(for a bit of context, I think /local initially appeared in ipfs/kubo#4666 (comment))

Copy link
Member

Choose a reason for hiding this comment

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

"files" is fine, really. or maybe just "file" to match the file:/// schema? My goal is just to disambiguate paths.

Should we support resolving /local outside Files API?

See ipfs/specs#98 and ipfs/specs#98 (comment). The hope was to eventually find a way to unify these APIs.


For now, let's just push forward with what we have. I think this is a bikeshed for another day.

// String returns the path as a string.
String() string

// Mutable returns false if the data pointed to by this path in guaranteed
// to not change.
//
// Note that resolved mutable path can be immutable.
Mutable() bool
}

type filesPath struct {
p string
}

func (p *filesPath) String() string {
return p.p
}

func (p *filesPath) Mutable() bool {
return !(strings.HasPrefix(p.p, "/ipfs") || strings.HasPrefix(p.p, "/ipld"))
}

func FilePath(p string) MfsPath {
// TODO: more validation
return &filesPath{
p: p,
}
}

type MfsAPI interface {
magik6k marked this conversation as resolved.
Show resolved Hide resolved
Create(ctx context.Context, path MfsPath) (File, error)
Open(ctx context.Context, path MfsPath) (File, error)
OpenFile(ctx context.Context, path MfsPath, flag int, perm os.FileMode) (File, error)

Stat(ctx context.Context, path MfsPath) (os.FileInfo, error)
Copy link
Member Author

Choose a reason for hiding this comment

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

TODO: change this to match ipfs files stat better

Copy link
Member

Choose a reason for hiding this comment

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

Maybe but I'd consider not materializing the CID here for performance reasons. IMO, files stat command should call Flush and Stat.

(although... there are some atomicity issues there...)


Rename(ctx context.Context, oldpath, newpath MfsPath) error
Remove(ctx context.Context, path MfsPath) error

// ReadDir reads the directory named by dirname and returns a list of
// directory entries sorted by filename.
ReadDir(ctx context.Context, path MfsPath) ([]os.FileInfo, error)
// MkdirAll creates a directory named path, along with any necessary
// parents, and returns nil, or else returns an error. The permission bits
// perm are used for all directories that MkdirAll creates. If path is
// already a directory, MkdirAll does nothing and returns nil.
MkdirAll(ctx context.Context, path MfsPath, perm os.FileMode) error

Flush(ctx context.Context, path MfsPath) error
Copy link
Member

Choose a reason for hiding this comment

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

We might want to make this return the CID.

// TODO: ChCid
// TODO: Symlink stuff (is it implemented in mfs?)
}

type File interface {
io.Writer
io.WriterAt
io.Reader
io.Seeker
io.Closer

Name() MfsPath
magik6k marked this conversation as resolved.
Show resolved Hide resolved

// Truncate the file.
Truncate(size int64) error
}

var _ MfsPath = (Path)(nil)
var _ MfsPath = (ResolvedPath)(nil)
1 change: 1 addition & 0 deletions tests/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func TestApi(p Provider) func(t *testing.T) {
t.Run("Key", tp.TestKey)
t.Run("Name", tp.TestName)
t.Run("Object", tp.TestObject)
t.Run("Mfs", tp.TestMfs)
t.Run("Path", tp.TestPath)
t.Run("Pin", tp.TestPin)
t.Run("PubSub", tp.TestPubSub)
Expand Down
27 changes: 27 additions & 0 deletions tests/mfs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package tests

import (
"context"
"testing"

"github.com/ipfs/interface-go-ipfs-core"
)

func (tp *provider) TestMfs(t *testing.T) {
t.Run("TestFilesDirs", tp.TestFilesDirs)
}

func (tp *provider) TestFilesDirs(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
api, err := tp.makeAPI(ctx)
if err != nil {
t.Fatal(err)
return
}

l, err := api.Files().ReadDir(ctx, iface.FilePath("/test"))
if len(l) != 0 {
return
}
}