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

Initial mount draft/WIP #6036

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -7,6 +7,13 @@ CLEAN += $(IPFS_BIN_$(d))

PATH := $(realpath $(d)):$(PATH)

#TODO: review; probably a more elegant way to do this; alternative is fork cgofuse, removing cgo invocations
ifeq ($(WINDOWS),1)
ifeq ($(origin CPATH), undefined)
export CGO_ENABLED = 0
endif
endif

# disabled for now
# depend on *.pb.go files in the repo as Order Only (as they shouldn't be rebuilt if exist)
# DPES_OO_$(d) := diagnostics/pb/diagnostics.pb.go exchange/bitswap/message/pb/message.pb.go
@@ -8,6 +8,7 @@ import (
"net/http"
_ "net/http/pprof"
"os"
"path/filepath"
"runtime"
"sort"
"sync"
@@ -17,19 +18,19 @@ import (
oldcmds "github.com/ipfs/go-ipfs/commands"
"github.com/ipfs/go-ipfs/core"
commands "github.com/ipfs/go-ipfs/core/commands"
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
mount "github.com/ipfs/go-ipfs/core/commands/mount"
"github.com/ipfs/go-ipfs/core/coreapi"
corehttp "github.com/ipfs/go-ipfs/core/corehttp"
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
nodeMount "github.com/ipfs/go-ipfs/fuse/node"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
migrate "github.com/ipfs/go-ipfs/repo/fsrepo/migrations"

goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
"gx/ipfs/QmTQuFQWHAWy4wMH6ZyPfGiawA5u9T8rs79FENoV8yXaoS/client_golang/prometheus"
ma "gx/ipfs/QmTZBfrPJmjWsCvHEtX5FE6KimVJhsJg5sBbqEFYf4UZtL/go-multiaddr"
cmds "gx/ipfs/QmX6AchyJgso1WNamTJMdxfzGiWuYu94K6tF9MJ66rRhAu/go-ipfs-cmds"
"gx/ipfs/Qmc85NSvmSG4Frn9Vb2cBc1rMyULH6D3TNVEfCzSKoUpip/go-multiaddr-net"
"gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
manet "gx/ipfs/Qmc85NSvmSG4Frn9Vb2cBc1rMyULH6D3TNVEfCzSKoUpip/go-multiaddr-net"
cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
mprome "gx/ipfs/QmfHYMtNSntM6qFhHzLDCyqTX7NNpsfwFgvicJv7L5saAP/go-metrics-prometheus"
)

@@ -39,7 +40,6 @@ const (
initOptionKwd = "init"
initProfileOptionKwd = "init-profile"
ipfsMountKwd = "mount-ipfs"
ipnsMountKwd = "mount-ipns"
migrateKwd = "migrate"
mountKwd = "mount"
offlineKwd = "offline" // global option
@@ -158,7 +158,6 @@ Headers.
cmdkit.BoolOption(mountKwd, "Mounts IPFS to the filesystem"),
cmdkit.BoolOption(writableKwd, "Enable writing objects (with POST, PUT and DELETE)"),
cmdkit.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount). Defaults to config setting."),
cmdkit.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount). Defaults to config setting."),
cmdkit.BoolOption(unrestrictedApiAccessKwd, "Allow API access to unlisted hashes"),
cmdkit.BoolOption(unencryptTransportKwd, "Disable transport encryption (for debugging protocols)"),
cmdkit.BoolOption(enableGCKwd, "Enable automatic periodic repo garbage collection"),
@@ -377,9 +376,6 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment

// construct fuse mountpoints - if the user provided the --mount flag
mount, _ := req.Options[mountKwd].(bool)
if mount && offline {
return cmdkit.Errorf(cmdkit.ErrClient, "mount is not currently supported in offline mode")
}
if mount {
if err := mountFuse(req, cctx); err != nil {
return err
@@ -632,27 +628,40 @@ func mountFuse(req *cmds.Request, cctx *oldcmds.Context) error {
return fmt.Errorf("mountFuse: GetConfig() failed: %s", err)
}

fsdir, found := req.Options[ipfsMountKwd].(string)
//FIXME: on MacOS supplying --ipfs-mount="~/ipfs-mount" creates a relative directory "./~/ipfs-mount"
mountPoint, found := req.Options[ipfsMountKwd].(string)
if !found {
fsdir = cfg.Mounts.IPFS
mountPoint = cfg.Mounts.IPFS
}

nsdir, found := req.Options[ipnsMountKwd].(string)
if !found {
nsdir = cfg.Mounts.IPNS
daemon, err := cctx.ConstructNode()
if err != nil {
return fmt.Errorf("mountFuse: ConstructNode() failed: %s", err)
}

node, err := cctx.ConstructNode()
//TODO: remove this when done debugging
if daemon.Mount != nil {
return fmt.Errorf("Programmer error detected; daemon is initializing but mountpoint is already set: {%p}%q", daemon.Mount, daemon.Mount.Where())
}

api, err := coreapi.NewCoreAPI(daemon)
if err != nil {
return fmt.Errorf("mountFuse: ConstructNode() failed: %s", err)
return fmt.Errorf("mountFuse: NewCoreAPI() failed: %s", err)
}

err = nodeMount.Mount(node, fsdir, nsdir)
fsi, err := mount.InvokeMount(mountPoint, daemon.FilesRoot, api, cctx.Context())
if err != nil {
return err
return fmt.Errorf("mountFuse: InvokeMount() failed: %s", err)
}
fmt.Printf("IPFS mounted at: %s\n", fsdir)
fmt.Printf("IPNS mounted at: %s\n", nsdir)

daemon.Mount = fsi

absMount, err := filepath.Abs(mountPoint)
if err != nil {
absMount = mountPoint
}

fmt.Printf("IPFS mounted at: %s\n", absMount)
return nil
}

@@ -1,27 +1,22 @@
// +build !windows,!nofuse
// +build !nofuse

package commands

import (
"errors"
"fmt"
"io"

cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
nodeMount "github.com/ipfs/go-ipfs/fuse/node"
"github.com/ipfs/go-ipfs/core/commands/cmdenv"
mount "github.com/ipfs/go-ipfs/core/commands/mount"
"github.com/ipfs/go-ipfs/core/coreapi"

config "gx/ipfs/QmUAuYuiafnJRZxDDX7MuruMNsicYNuyub5vUeAcupUBNs/go-ipfs-config"
cmds "gx/ipfs/QmX6AchyJgso1WNamTJMdxfzGiWuYu94K6tF9MJ66rRhAu/go-ipfs-cmds"
cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
)

const (
mountIPFSPathOptionName = "ipfs-path"
mountIPNSPathOptionName = "ipns-path"
)

var MountCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Mounts IPFS to the filesystem (read-only).",
Tagline: "Mounts IPFS to the filesystem.",
ShortDescription: `
Mount IPFS at a read-only mountpoint on the OS (default: /ipfs and /ipns).
All IPFS objects will be accessible under that directory. Note that the
@@ -77,53 +72,60 @@ baz
`,
},
Options: []cmdkit.Option{
cmdkit.StringOption(mountIPFSPathOptionName, "f", "The path where IPFS should be mounted."),
cmdkit.StringOption(mountIPNSPathOptionName, "n", "The path where IPNS should be mounted."),
cmdkit.StringOption("ipfs-path", "f", "The path where IPFS should be mounted."),
//TODO: this should probably be an argument not an option; or possibly its own command `ipfs unmount [-f --timeout]`
cmdkit.BoolOption("unmount", "u", "Destroy existing mount."),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
cfg, err := cmdenv.GetConfig(env)
if err != nil {
return err
}

nd, err := cmdenv.GetNode(env)
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) (err error) {
defer res.Close()

daemon, err := cmdenv.GetNode(env)
if err != nil {
return err
}

// error if we aren't running node in online mode
if nd.LocalMode() {
return ErrNotOnline
destroy, _ := req.Options["unmount"].(bool)
if destroy {
if daemon.Mount == nil {
return errors.New("IPFS is not mounted")
}
whence := daemon.Mount.Where()
ret := daemon.Mount.Close()
if ret == nil {
cmds.EmitOnce(res, fmt.Sprintf("Successfully unmounted %#q", whence))
daemon.Mount = nil
}

return ret
}

fsdir, found := req.Options[mountIPFSPathOptionName].(string)
if !found {
fsdir = cfg.Mounts.IPFS // use default value
if daemon.Mount != nil {
//TODO: introduce `mount -f` to automatically do this?
//problem: '-f' overlaps with ipfs-path short param
return fmt.Errorf("IPFS already mounted at: %#q use `ipfs mount -u`", daemon.Mount.Where())
}

// get default mount points
nsdir, found := req.Options[mountIPNSPathOptionName].(string)
if !found {
nsdir = cfg.Mounts.IPNS // NB: be sure to not redeclare!
api, err := coreapi.NewCoreAPI(daemon)
if err != nil {
return err
}

err = nodeMount.Mount(nd, fsdir, nsdir)
conf, err := cmdenv.GetConfig(env)
if err != nil {
return err
}

var output config.Mounts
output.IPFS = fsdir
output.IPNS = nsdir
return cmds.EmitOnce(res, &output)
},
Type: config.Mounts{},
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, mounts *config.Mounts) error {
fmt.Fprintf(w, "IPFS mounted at: %s\n", mounts.IPFS)
fmt.Fprintf(w, "IPNS mounted at: %s\n", mounts.IPNS)

return nil
}),
daemonCtx := daemon.Context()
mountPoint := conf.Mounts.IPFS
filesRoot := daemon.FilesRoot

fsi, err := mount.InvokeMount(mountPoint, filesRoot, api, daemonCtx)
if err != nil {
return err
}
daemon.Mount = fsi
cmds.EmitOnce(res, fmt.Sprintf("mounted at: %#q", daemon.Mount.Where()))
return nil
},
}
@@ -0,0 +1,97 @@
package fusemount

import (
cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid"
mh "gx/ipfs/QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW/go-multihash"

lru "gx/ipfs/QmQjMHF8ptRgx4E57UFMiT4YM6kqaJeYxZ1MCDX23aw4rK/golang-lru"
)

/* OLD
type cidCache interface {
Add(*cid.Cid, fusePath)
Request(*cid.Cid) fusePath
Release(*cid.Cid)
}
*/

type cidCache struct {
actual *lru.ARCCache
cb cid.Builder
}

func (cc *cidCache) Add(nCid cid.Cid, fp fusePath) {
if cidCacheEnabled {
cc.actual.Add(nCid, fp)
}
}

func (cc *cidCache) Request(nCid cid.Cid) fusePath {
if !cidCacheEnabled {
return nil
}
if v, ok := cc.actual.Get(nCid); ok {
if _, ok = v.(fusePath); !ok {
log.Errorf("Cache entry for %q is not valid: {%T}%#v", nCid, v, v)
return nil
}
return v.(fusePath)
}
return nil
}

func (cc *cidCache) Release(nCid cid.Cid) {
if !cidCacheEnabled {
return
}
cc.actual.Remove(nCid)
}

//TODO: size from conf
func (cc *cidCache) Init() error {
if !cidCacheEnabled {
return nil
}

arc, err := lru.NewARC(100) //NOTE: arbitrary debug size
if err != nil {
return err
}
cc.actual = arc
//TODO: pkg/cid should expose a recommendation hint, i.e. cid.RecommendedDefault
cc.cb = cid.NewPrefixV1(cid.Raw, mh.SHA2_256)
return nil
}

func (cc *cidCache) ReleasePath(path string) {
if !cidCacheEnabled {
return
}

pathCid, err := cc.cb.Sum([]byte(path))
if err != nil {
log.Errorf("Cache - hash error [report this]: %s", err)
return
}
cc.actual.Remove(pathCid)
}

func (cc *cidCache) RequestPath(path string) fusePath {
if !cidCacheEnabled {
return nil
}

pathCid, err := cc.cb.Sum([]byte(path))
if err != nil {
log.Errorf("Cache - hash error [report this]: %s", err)
return nil
}
if node, ok := cc.actual.Get(pathCid); ok {
return node.(fusePath)
}
return nil
}

func (cc *cidCache) Hash(path string) (cid.Cid, error) {
return cc.cb.Sum([]byte(path))
}