mount_unix.go 4.01 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// +build linux darwin freebsd

package commands

import (
	"fmt"
	"time"

	cmds "github.com/jbenet/go-ipfs/commands"
	"github.com/jbenet/go-ipfs/config"
	core "github.com/jbenet/go-ipfs/core"
	ipns "github.com/jbenet/go-ipfs/fuse/ipns"
	rofs "github.com/jbenet/go-ipfs/fuse/readonly"
)

// amount of time to wait for mount errors
17
// TODO is this non-deterministic?
18 19 20
const mountTimeout = time.Second

var mountCmd = &cmds.Command{
21 22 23
	Helptext: cmds.HelpText{
		Tagline: "Mounts IPFS to the filesystem (read-only)",
		ShortDescription: `
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
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
root will not be listable, as it is virtual. Access known paths directly.

You may kave to create /ipfs and /ipfs before using 'ipfs mount':

> sudo mkdir /ipfs /ipns
> sudo chown ` + "`" + `whoami` + "`" + ` /ipfs /ipns
> ipfs mount
`,
		LongDescription: `
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
root will not be listable, as it is virtual. Access known paths directly.

> sudo mkdir /ipfs /ipns
> sudo chown ` + "`" + `whoami` + "`" + ` /ipfs /ipns
> ipfs mount

EXAMPLE:

# setup
> mkdir foo
> echo "baz" > foo/bar
> ipfs add -r foo
added QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR foo/bar
added QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC foo
> ipfs ls QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC
QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR 12 bar
> ipfs cat QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR
baz

# mount
> ipfs daemon &
> ipfs mount
IPFS mounted at: /ipfs
IPNS mounted at: /ipns
> cd /ipfs/QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC
> ls
bar
> cat bar
baz
> cat /ipfs/QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC/bar
baz
> cat /ipfs/QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR
baz
70
`,
71
	},
72

73
	Options: []cmds.Option{
Brian Tiger Chow's avatar
Brian Tiger Chow committed
74
		// TODO longform
75
		cmds.StringOption("f", "The path where IPFS should be mounted"),
Brian Tiger Chow's avatar
Brian Tiger Chow committed
76 77

		// TODO longform
78
		cmds.StringOption("n", "The path where IPNS should be mounted"),
79
	},
80
	Run: func(req cmds.Request) (interface{}, error) {
81 82 83 84 85 86 87 88 89
		cfg, err := req.Context().GetConfig()
		if err != nil {
			return nil, err
		}

		node, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}
90 91

		// error if we aren't running node in online mode
92
		if node.Network == nil {
93
			return nil, errNotOnline
94 95 96
		}

		if err := platformFuseChecks(); err != nil {
97
			return nil, err
98 99
		}

100 101 102 103 104
		fsdir, found, err := req.Option("f").String()
		if err != nil {
			return nil, err
		}
		if !found {
105
			fsdir = cfg.Mounts.IPFS // use default value
106
		}
107
		fsdone := mountIpfs(node, fsdir)
108 109

		// get default mount points
110 111 112
		nsdir, found, err := req.Option("n").String()
		if err != nil {
			return nil, err
113
		}
114
		if !found {
115
			nsdir = cfg.Mounts.IPNS // NB: be sure to not redeclare!
116 117
		}

118
		nsdone := mountIpns(node, nsdir, fsdir)
119 120 121

		// wait until mounts return an error (or timeout if successful)
		select {
122 123 124 125
		case err := <-fsdone:
			return nil, err
		case err := <-nsdone:
			return nil, err
126 127 128

		// mounted successfully, we timed out with no errors
		case <-time.After(mountTimeout):
129
			output := cfg.Mounts
130
			return &output, nil
131 132 133
		}
	},
	Type: &config.Mounts{},
134
	Marshalers: cmds.MarshalerMap{
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
		cmds.Text: func(res cmds.Response) ([]byte, error) {
			v := res.Output().(*config.Mounts)
			s := fmt.Sprintf("IPFS mounted at: %s\n", v.IPFS)
			s += fmt.Sprintf("IPNS mounted at: %s\n", v.IPNS)
			return []byte(s), nil
		},
	},
}

func mountIpfs(node *core.IpfsNode, fsdir string) <-chan error {
	done := make(chan error)
	log.Info("Mounting IPFS at ", fsdir)

	go func() {
		err := rofs.Mount(node, fsdir)
		done <- err
		close(done)
	}()

	return done
}

func mountIpns(node *core.IpfsNode, nsdir, fsdir string) <-chan error {
	if nsdir == "" {
		return nil
	}
	done := make(chan error)
	log.Info("Mounting IPNS at ", nsdir)

	go func() {
		err := ipns.Mount(node, nsdir, fsdir)
		done <- err
		close(done)
	}()

	return done
}

var platformFuseChecks = func() error {
	return nil
}