From e9bc92362b1bb68803b4afd39a112dfacee70ca1 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Sun, 6 Jul 2014 02:27:02 -0700 Subject: [PATCH] fuse using github.com/hanwen/go-fuse --- cli/ipfs.go | 1 + cli/mount.go | 47 +++++++++++++++++ fuse/readonly/readonly.go | 105 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 cli/mount.go create mode 100644 fuse/readonly/readonly.go diff --git a/cli/ipfs.go b/cli/ipfs.go index 1fcbbf154..e970ef634 100644 --- a/cli/ipfs.go +++ b/cli/ipfs.go @@ -42,6 +42,7 @@ Use "ipfs help " for more information about a command. cmdIpfsRefs, cmdIpfsVersion, cmdIpfsCommands, + cmdIpfsMount, }, Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError), } diff --git a/cli/mount.go b/cli/mount.go new file mode 100644 index 000000000..ac2327706 --- /dev/null +++ b/cli/mount.go @@ -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 - 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 +} diff --git a/fuse/readonly/readonly.go b/fuse/readonly/readonly.go new file mode 100644 index 000000000..1c238f131 --- /dev/null +++ b/fuse/readonly/readonly.go @@ -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 +}