mount_unix.go 2.62 KB
Newer Older
1
// +build !windows,!nofuse
2 3 4 5 6 7 8

package node

import (
	"errors"
	"fmt"
	"strings"
9
	"sync"
10 11 12 13 14

	core "github.com/ipfs/go-ipfs/core"
	ipns "github.com/ipfs/go-ipfs/fuse/ipns"
	mount "github.com/ipfs/go-ipfs/fuse/mount"
	rofs "github.com/ipfs/go-ipfs/fuse/readonly"
Steven Allen's avatar
Steven Allen committed
15

Jakub Sztandera's avatar
Jakub Sztandera committed
16
	logging "github.com/ipfs/go-log"
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
)

var log = logging.Logger("node")

// fuseNoDirectory used to check the returning fuse error
const fuseNoDirectory = "fusermount: failed to access mountpoint"

// fuseExitStatus1 used to check the returning fuse error
const fuseExitStatus1 = "fusermount: exit status 1"

// platformFuseChecks can get overridden by arch-specific files
// to run fuse checks (like checking the OSXFUSE version)
var platformFuseChecks = func(*core.IpfsNode) error {
	return nil
}

Łukasz Magiera's avatar
Łukasz Magiera committed
33 34
type errNeedFuseVersion error // used in tests, needed in OSX

35 36 37 38 39
func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
	// check if we already have live mounts.
	// if the user said "Mount", then there must be something wrong.
	// so, close them and try again.
	if node.Mounts.Ipfs != nil && node.Mounts.Ipfs.IsActive() {
40 41
		// best effort
		_ = node.Mounts.Ipfs.Unmount()
42 43
	}
	if node.Mounts.Ipns != nil && node.Mounts.Ipns.IsActive() {
44 45
		// best effort
		_ = node.Mounts.Ipns.Unmount()
46 47 48 49 50 51
	}

	if err := platformFuseChecks(node); err != nil {
		return err
	}

52
	return doMount(node, fsdir, nsdir)
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
}

func doMount(node *core.IpfsNode, fsdir, nsdir string) error {
	fmtFuseErr := func(err error, mountpoint string) error {
		s := err.Error()
		if strings.Contains(s, fuseNoDirectory) {
			s = strings.Replace(s, `fusermount: "fusermount:`, "", -1)
			s = strings.Replace(s, `\n", exit status 1`, "", -1)
			return errors.New(s)
		}
		if s == fuseExitStatus1 {
			s = fmt.Sprintf("fuse failed to access mountpoint %s", mountpoint)
			return errors.New(s)
		}
		return err
	}

	// this sync stuff is so that both can be mounted simultaneously.
71 72
	var fsmount, nsmount mount.Mount
	var err1, err2 error
73

74
	var wg sync.WaitGroup
75

76
	wg.Add(1)
77
	go func() {
78
		defer wg.Done()
79 80 81
		fsmount, err1 = rofs.Mount(node, fsdir)
	}()

82
	if node.IsOnline {
83
		wg.Add(1)
Jeromy's avatar
Jeromy committed
84
		go func() {
85
			defer wg.Done()
Jeromy's avatar
Jeromy committed
86 87 88
			nsmount, err2 = ipns.Mount(node, nsdir, fsdir)
		}()
	}
89

90
	wg.Wait()
91 92 93 94 95 96 97 98 99 100 101

	if err1 != nil {
		log.Errorf("error mounting: %s", err1)
	}

	if err2 != nil {
		log.Errorf("error mounting: %s", err2)
	}

	if err1 != nil || err2 != nil {
		if fsmount != nil {
102
			_ = fsmount.Unmount()
103 104
		}
		if nsmount != nil {
105
			_ = nsmount.Unmount()
106 107 108 109 110 111 112 113 114 115 116 117 118
		}

		if err1 != nil {
			return fmtFuseErr(err1, fsdir)
		}
		return fmtFuseErr(err2, nsdir)
	}

	// setup node state, so that it can be cancelled
	node.Mounts.Ipfs = fsmount
	node.Mounts.Ipns = nsmount
	return nil
}