mirror of
https://github.com/ipfs/kubo.git
synced 2025-06-29 17:36:38 +08:00
fuse using github.com/hanwen/go-fuse
This commit is contained in:
@ -42,6 +42,7 @@ Use "ipfs help <command>" for more information about a command.
|
|||||||
cmdIpfsRefs,
|
cmdIpfsRefs,
|
||||||
cmdIpfsVersion,
|
cmdIpfsVersion,
|
||||||
cmdIpfsCommands,
|
cmdIpfsCommands,
|
||||||
|
cmdIpfsMount,
|
||||||
},
|
},
|
||||||
Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError),
|
Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError),
|
||||||
}
|
}
|
||||||
|
47
cli/mount.go
Normal file
47
cli/mount.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gonuts/flag"
|
||||||
|
"github.com/jbenet/commander"
|
||||||
|
rofs "github.com/jbenet/go-ipfs/fuse/readonly"
|
||||||
|
u "github.com/jbenet/go-ipfs/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdIpfsMount = &commander.Command{
|
||||||
|
UsageLine: "mount",
|
||||||
|
Short: "Mount an ipfs read-only mountpoint.",
|
||||||
|
Long: `ipfs mount <os-path> - Mount an ipfs read-only mountpoint.
|
||||||
|
|
||||||
|
Mount ipfs at a read-only mountpoint on the OS. All ipfs objects
|
||||||
|
will be accessible under that directory. Note that the root will
|
||||||
|
not be listable, as it is virtual. Accessing known paths directly.
|
||||||
|
|
||||||
|
`,
|
||||||
|
Run: mountCmd,
|
||||||
|
Flag: *flag.NewFlagSet("ipfs-mount", flag.ExitOnError),
|
||||||
|
}
|
||||||
|
|
||||||
|
func mountCmd(c *commander.Command, inp []string) error {
|
||||||
|
if len(inp) < 1 || len(inp[0]) == 0 {
|
||||||
|
u.POut(c.Long)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := localNode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mp := inp[0]
|
||||||
|
fmt.Printf("Mounting at %s\n", mp)
|
||||||
|
|
||||||
|
fs := rofs.NewFileSystem(n)
|
||||||
|
s, err := rofs.Mount(fs, mp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.SetDebug(true)
|
||||||
|
s.Serve()
|
||||||
|
return nil
|
||||||
|
}
|
105
fuse/readonly/readonly.go
Normal file
105
fuse/readonly/readonly.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// A Go mirror of libfuse's hello.c
|
||||||
|
|
||||||
|
package readonly
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hanwen/go-fuse/fuse"
|
||||||
|
"github.com/hanwen/go-fuse/fuse/nodefs"
|
||||||
|
"github.com/hanwen/go-fuse/fuse/pathfs"
|
||||||
|
core "github.com/jbenet/go-ipfs/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileSystem struct {
|
||||||
|
Ipfs *core.IpfsNode
|
||||||
|
pathfs.FileSystem
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFileSystem(ipfs *core.IpfsNode) *FileSystem {
|
||||||
|
return &FileSystem{
|
||||||
|
Ipfs: ipfs,
|
||||||
|
FileSystem: pathfs.NewDefaultFileSystem(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FileSystem) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
|
||||||
|
if name == "/" { // -rw +x on root
|
||||||
|
return &fuse.Attr{Mode: fuse.S_IFDIR | 0111}, fuse.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
nd, err := s.Ipfs.Resolver.ResolvePath(name)
|
||||||
|
if err != nil {
|
||||||
|
// todo: make this error more versatile.
|
||||||
|
return nil, fuse.ENOENT
|
||||||
|
}
|
||||||
|
|
||||||
|
// links? say dir. could have data...
|
||||||
|
if len(nd.Links) > 0 {
|
||||||
|
return &fuse.Attr{Mode: fuse.S_IFDIR | 0555}, fuse.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
// size
|
||||||
|
size, _ := nd.Size()
|
||||||
|
|
||||||
|
// file.
|
||||||
|
return &fuse.Attr{
|
||||||
|
Mode: fuse.S_IFREG | 0444,
|
||||||
|
Size: uint64(size),
|
||||||
|
}, fuse.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FileSystem) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
|
||||||
|
if name == "/" { // nope
|
||||||
|
return nil, fuse.EPERM
|
||||||
|
}
|
||||||
|
|
||||||
|
nd, err := s.Ipfs.Resolver.ResolvePath(name)
|
||||||
|
if err != nil {
|
||||||
|
// todo: make this error more versatile.
|
||||||
|
return nil, fuse.ENOENT
|
||||||
|
}
|
||||||
|
|
||||||
|
entries := make([]fuse.DirEntry, len(nd.Links))
|
||||||
|
for i, link := range nd.Links {
|
||||||
|
n := link.Name
|
||||||
|
if len(n) == 0 {
|
||||||
|
n = link.Hash.B58String()
|
||||||
|
}
|
||||||
|
entries[i] = fuse.DirEntry{Name: n, Mode: fuse.S_IFREG | 0444}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(entries) > 0 {
|
||||||
|
return entries, fuse.OK
|
||||||
|
}
|
||||||
|
return nil, fuse.ENOENT
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FileSystem) Open(name string, flags uint32, context *fuse.Context) (
|
||||||
|
file nodefs.File, code fuse.Status) {
|
||||||
|
|
||||||
|
// read only, bro!
|
||||||
|
if flags&fuse.O_ANYWRITE != 0 {
|
||||||
|
return nil, fuse.EPERM
|
||||||
|
}
|
||||||
|
|
||||||
|
nd, err := s.Ipfs.Resolver.ResolvePath(name)
|
||||||
|
if err != nil {
|
||||||
|
// todo: make this error more versatile.
|
||||||
|
return nil, fuse.ENOENT
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodefs.NewDataFile([]byte(nd.Data)), fuse.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FileSystem) String() string {
|
||||||
|
return "IpfsReadOnly"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FileSystem) OnMount(nodeFs *pathfs.PathNodeFs) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func Mount(s *FileSystem, path string) (*fuse.Server, error) {
|
||||||
|
rfs := pathfs.NewReadonlyFileSystem(s)
|
||||||
|
fs := pathfs.NewPathNodeFs(rfs, nil)
|
||||||
|
ser, _, err := nodefs.MountRoot(path, fs.Root(), nil)
|
||||||
|
return ser, err
|
||||||
|
}
|
Reference in New Issue
Block a user