fuse: no longer require fuse to compile ipfs
This commit removes the dependency on go-fuse-version, and thus the fuse headers. It also introduces an elaborate troubleshooting process that diagnoses whether fuse installed -- and which version -- with as little requirements as possible (attept to use sysctl, fall-back on the go-fuse-version binary, etc). It then nicely instructs the user what to do next.
Juan Batiz-Benet committed
Jan 28, 2015 at 05:18 UTC
7ae3706b6d8c13e61b2f829e7296fc471401ce2d
16 files changed
+326
-72
Godeps/Godeps.json
+1
-1
@@ -147,7 +147,7 @@
147
},
148
{
149
"ImportPath": "github.com/jbenet/go-fuse-version",
150
- "Rev": "c723f93ceeb1d1e21eb7fe6fd39aa21a9fe7db99"
150
+ "Rev": "b733dfc0597e1f6780510ee7afad8b6e3c7af3eb"
151
},
152
{
153
"ImportPath": "github.com/jbenet/go-is-domain",
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/README.md
+5
-5
@@ -32,14 +32,14 @@ func main() {
32
}
33
```
34
35
-## fuseprint
35
+## fuse-print
36
37
-If you dont use Go, you can also install the example as the silly util fuseprint:
37
+If you dont use Go, you can also install the example as the silly util `fuse-version`:
38
39
```
40
-> go get github.com/jbenet/go-fuse-version/fuseprint
41
-> go install github.com/jbenet/go-fuse-version/fuseprint
42
-> fuseprint
40
+> go get github.com/jbenet/go-fuse-version/fuse-version
41
+> go install github.com/jbenet/go-fuse-version/fuse-version
42
+> fuse-version
43
FuseVersion, AgentVersion, Agent
44
27, 2.7.2, OSXFUSE
45
```
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/fuse-version/.gitignore
new
+1
@@ -0,0 +1 @@
1
+fuse-version
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/fuse-version/README.md
renamed
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/fuse-version/index.go
new
+106
@@ -0,0 +1,106 @@
1
+package main
2
+
3
+import (
4
+ "bytes"
5
+ "flag"
6
+ "fmt"
7
+ "os"
8
+
9
+ fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
10
+)
11
+
12
+// flags
13
+var (
14
+ flagSystem string
15
+ flagOnly string
16
+ flagQuiet bool
17
+)
18
+
19
+var usage = `usage: %s [flags]
20
+print fuse and fuse agent versions
21
+`
22
+
23
+func init() {
24
+ flag.Usage = func() {
25
+ fmt.Fprintf(os.Stderr, usage, os.Args[0])
26
+ flag.PrintDefaults()
27
+ }
28
+
29
+ flag.StringVar(&flagSystem, "s", "", "show only one system (e.g. OSXFUSE)")
30
+ flag.StringVar(&flagOnly, "only", "", "show one of {fuse, agent, agent-name}")
31
+ flag.BoolVar(&flagQuiet, "q", false, "quiet output, no newline (use with --only)")
32
+}
33
+
34
+func main() {
35
+ flag.Parse()
36
+ sys, err := fuseversion.LocalFuseSystems()
37
+ if err != nil {
38
+ fmt.Fprintf(os.Stderr, "%s\n", err)
39
+ os.Exit(1)
40
+ }
41
+
42
+ all := flagSystem == ""
43
+
44
+ // if user specified a system and we dont have it, error out.
45
+ if !all {
46
+ checkExists(sys, flagSystem)
47
+ }
48
+
49
+ var buf bytes.Buffer
50
+ for name, s := range sys {
51
+ if !all && flagSystem != name {
52
+ continue
53
+ }
54
+
55
+ switch flagOnly {
56
+ case "fuse":
57
+ fmt.Fprintf(&buf, PartString(name, "FuseVersion", s.FuseVersion))
58
+ case "agent":
59
+ fmt.Fprintf(&buf, PartString(name, "AgentVersion", s.AgentVersion))
60
+ case "agent-name":
61
+ fmt.Fprintf(&buf, PartString(name, "AgentName", s.AgentName))
62
+ default:
63
+ fmt.Fprintf(&buf, SystemString(name, s))
64
+ }
65
+
66
+ if all && flagQuiet { // if all & quiet, need to break between systems
67
+ fmt.Fprintf(&buf, "\n")
68
+ }
69
+ }
70
+
71
+ out := buf.Bytes()
72
+ if flagQuiet {
73
+ out = bytes.TrimSpace(out)
74
+ }
75
+ os.Stdout.Write(out)
76
+}
77
+
78
+func checkExists(all fuseversion.Systems, name string) {
79
+
80
+ if _, found := all[name]; found {
81
+ return
82
+ }
83
+
84
+ if !flagQuiet {
85
+ fmt.Fprintf(os.Stderr, "error: %s system not found.\nHave: ")
86
+ for name := range all {
87
+ fmt.Fprintf(os.Stderr, "%s ", name)
88
+ }
89
+ fmt.Fprintf(os.Stderr, "\n")
90
+ }
91
+ os.Exit(1)
92
+}
93
+
94
+func SystemString(name string, sys fuseversion.FuseSystem) (s string) {
95
+ s += PartString(name, "FuseVersion", sys.FuseVersion)
96
+ s += PartString(name, "AgentVersion", sys.AgentVersion)
97
+ s += PartString(name, "AgentName", sys.AgentName)
98
+ return s
99
+}
100
+
101
+func PartString(sysname, partname, part string) string {
102
+ if !flagQuiet {
103
+ return fmt.Sprintf("%s.%s: %s\n", sysname, partname, part)
104
+ }
105
+ return part + "\t"
106
+}
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/fuseprint/.gitignore
deleted
-1
@@ -1 +0,0 @@
1
-fuseprint
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/fuseprint/index.go
deleted
-21
@@ -1,21 +0,0 @@
1
-package main
2
-
3
-import (
4
- "fmt"
5
- "os"
6
-
7
- fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
8
-)
9
-
10
-func main() {
11
- sys, err := fuseversion.LocalFuseSystems()
12
- if err != nil {
13
- fmt.Fprintf(os.Stderr, "%s\n", err)
14
- os.Exit(1)
15
- }
16
-
17
- fmt.Printf("FuseVersion, AgentVersion, Agent\n")
18
- for _, s := range *sys {
19
- fmt.Printf("%s, %s, %s\n", s.FuseVersion, s.AgentVersion, s.AgentName)
20
- }
21
-}
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/version.go
+1
-1
@@ -29,7 +29,7 @@ type FuseSystem struct {
29
// // Outputs:
30
// // OSXFUSE, , 2.7.2
31
//
32
-func LocalFuseSystems() (*Systems, error) {
32
+func LocalFuseSystems() (Systems, error) {
33
return getLocalFuseSystems() // implemented by each platform
34
}
35
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/version_bsd.go
+1
-1
@@ -7,6 +7,6 @@ import (
7
"runtime"
8
)
9
10
-func getLocalFuseSystems() (*Systems, error) {
10
+func getLocalFuseSystems() (Systems, error) {
11
return nil, fmt.Errorf(notImplYet, runtime.GOARCH)
12
}
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/version_darwin.go
+3
-3
@@ -9,10 +9,10 @@ package fuseversion
9
import "C"
10
import "fmt"
11
12
-func getLocalFuseSystems() (*Systems, error) {
13
- sys := Systems{}
12
+func getLocalFuseSystems() (Systems, error) {
13
+ sys := make(Systems)
14
sys["OSXFUSE"] = getOSXFUSE()
15
- return &sys, nil
15
+ return sys, nil
16
}
17
18
func getOSXFUSE() FuseSystem {
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/version_linux.go
+1
-1
@@ -5,6 +5,6 @@ import (
5
"runtime"
6
)
7
8
-func getLocalFuseSystems() (*Systems, error) {
8
+func getLocalFuseSystems() (Systems, error) {
9
return nil, fmt.Errorf(notImplYet, runtime.GOARCH)
10
}
Godeps/_workspace/src/github.com/jbenet/go-fuse-version/version_windows.go
+1
-1
@@ -5,6 +5,6 @@ import (
5
"runtime"
6
)
7
8
-func getLocalFuseSystems() (*Systems, error) {
8
+func getLocalFuseSystems() (Systems, error) {
9
return nil, fmt.Errorf(notImplYet, runtime.GOARCH)
10
}
core/commands/internal/incfusever/incfusever.go
new
+13
@@ -0,0 +1,13 @@
1
+// Package incfusever is only here to prevent go src tools (like godep)
2
+// from thinking fuseversion is not a required package. Though we do not
3
+// actually use github.com/jbenet/go-fuse-version as a library, we
4
+// _may_ need its binary. We avoid it as much as possible as compiling
5
+// it _requires_ fuse headers. Users must be able to install go-ipfs
6
+// without also installing fuse.
7
+package incfusever
8
+
9
+import (
10
+ fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
11
+)
12
+
13
+var _ = fuseversion.LocalFuseSystems
core/commands/mount.go
deleted
-9
@@ -1,9 +0,0 @@
1
-package commands
2
-
3
-import (
4
- fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
5
-)
6
-
7
-// this file is only here to prevent go src tools (like godep) from
8
-// thinking fuseversion is not a required package by non-darwin archs.
9
-var _ = fuseversion.LocalFuseSystems
core/commands/mount_darwin.go
+186
-23
@@ -1,12 +1,14 @@
1
package commands
2
3
import (
4
+ "bytes"
5
"fmt"
6
+ "os/exec"
7
"runtime"
8
"strings"
9
"syscall"
10
9
- fuseversion "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-fuse-version"
11
+ core "github.com/jbenet/go-ipfs/core"
12
)
13
14
func init() {
@@ -14,19 +16,122 @@ func init() {
16
platformFuseChecks = darwinFuseCheckVersion
17
}
18
17
-func darwinFuseCheckVersion() error {
19
+// dontCheckOSXFUSEConfigKey is a key used to let the user tell us to
20
+// skip fuse checks.
21
+var dontCheckOSXFUSEConfigKey = "DontCheckOSXFUSE"
22
+
23
+// fuseVersionPkg is the go pkg url for fuse-version
24
+var fuseVersionPkg = "github.com/jbenet/go-fuse-version/fuse-version"
25
+
26
+// errStrFuseRequired is returned when we're sure the user does not have fuse.
27
+var errStrFuseRequired = `OSXFUSE not found.
28
+
29
+OSXFUSE is required to mount, please install it.
30
+Note: version 2.7.2 or higher required; prior versions are known to kernel panic!
31
+It is recommended you install it from the OSXFUSE website:
32
+
33
+ http://osxfuse.github.io/
34
+
35
+For more help, see:
36
+
37
+ https://github.com/jbenet/go-ipfs/issues/177
38
+`
39
+
40
+// errStrNoFuseHeaders is included in the output of `go get <fuseVersionPkg>` if there
41
+// are no fuse headers. this means they dont have OSXFUSE installed.
42
+var errStrNoFuseHeaders = "no such file or directory: '/usr/local/lib/libosxfuse.dylib'"
43
+
44
+var errStrUpgradeFuse = `OSXFUSE version %s not supported.
45
+
46
+OSXFUSE versions <2.7.2 are known to cause kernel panics!
47
+Please upgrade to the latest OSXFUSE version.
48
+It is recommended you install it from the OSXFUSE website:
49
+
50
+ http://osxfuse.github.io/
51
+
52
+For more help, see:
53
+
54
+ https://github.com/jbenet/go-ipfs/issues/177
55
+`
56
+
57
+var errStrNeedFuseVersion = `unable to check fuse version.
58
+
59
+Dear User,
60
+
61
+Before mounting, we must check your version of OSXFUSE. We are protecting
62
+you from a nasty kernel panic we found in OSXFUSE versions <2.7.2.[1]. To
63
+make matters worse, it's harder than it should be to check whether you have
64
+the right version installed...[2]. We've automated the process with the
65
+help of a little tool. We tried to install it, but something went wrong[3].
66
+Please install it yourself by running:
67
+
68
+ go get %s
69
+
70
+You can also stop ipfs from running these checks and use whatever OSXFUSE
71
+version you have by running:
72
+
73
+ ipfs config %s true
74
+
75
+[1]: https://github.com/jbenet/go-ipfs/issues/177
76
+[2]: https://github.com/jbenet/go-ipfs/pull/533
77
+[3]: %s
78
+`
79
+
80
+var errStrFailedToRunFuseVersion = `unable to check fuse version.
81
+
82
+Dear User,
83
+
84
+Before mounting, we must check your version of OSXFUSE. We are protecting
85
+you from a nasty kernel panic we found in OSXFUSE versions <2.7.2.[1]. To
86
+make matters worse, it's harder than it should be to check whether you have
87
+the right version installed...[2]. We've automated the process with the
88
+help of a little tool. We tried to run it, but something went wrong[3].
89
+Please, try to run it yourself with:
90
+
91
+ go get %s
92
+ fuse-version
93
+
94
+You should see something like this:
95
+
96
+ > fuse-version
97
+ fuse-version -only agent
98
+ OSXFUSE.AgentVersion: 2.7.3
99
+
100
+Just make sure the number is 2.7.2 or higher. You can then stop ipfs from
101
+trying to run these checks with:
102
+
103
+ ipfs config %s true
104
+
105
+[1]: https://github.com/jbenet/go-ipfs/issues/177
106
+[2]: https://github.com/jbenet/go-ipfs/pull/533
107
+[3]: %s
108
+`
109
+
110
+var errStrFixConfig = `config key invalid: %s %s
111
+You may be able to get this error to go away by setting it again:
112
+
113
+ ipfs config %s true
114
+
115
+Either way, please tell us at: http://github.com/jbenet/go-ipfs/issues
116
+`
117
+
118
+func darwinFuseCheckVersion(node *core.IpfsNode) error {
119
// on OSX, check FUSE version.
120
if runtime.GOOS != "darwin" {
121
return nil
122
}
123
23
- ov, err := tryGFV()
24
- if err != nil {
25
- log.Debug(err)
26
- ov, err = trySysctl()
27
- if err != nil {
28
- log.Debug(err)
29
- return fmt.Errorf("cannot determine osxfuse version. is it installed?")
124
+ ov, errGFV := tryGFV()
125
+ if errGFV != nil {
126
+ // if we failed AND the user has told us to ignore the check we
127
+ // continue. this is in case fuse-version breaks or the user cannot
128
+ // install it, but is sure their fuse version will work.
129
+ if skip, err := userAskedToSkipFuseCheck(node); err != nil {
130
+ return err
131
+ } else if skip {
132
+ return nil // user told us not to check version... ok....
133
+ } else {
134
+ return errGFV
135
}
136
}
137
@@ -35,25 +140,18 @@ func darwinFuseCheckVersion() error {
140
return nil
141
}
142
38
- return fmt.Errorf("osxfuse version %s not supported.\n%s\n%s", ov,
39
- "Older versions of osxfuse have kernel panic bugs; please upgrade!",
40
- "https://github.com/jbenet/go-ipfs/issues/177")
143
+ return fmt.Errorf(errStrUpgradeFuse, ov)
144
}
145
146
func tryGFV() (string, error) {
44
- sys, err := fuseversion.LocalFuseSystems()
45
- if err != nil {
46
- log.Debug("mount: fuseversion:", "failed")
47
- return "", err
48
- }
49
-
50
- for _, s := range *sys {
51
- v := s.AgentVersion
52
- log.Debug("mount: fuseversion:", v)
53
- return v, nil
147
+ // first try sysctl. it may work!
148
+ ov, err := trySysctl()
149
+ if err == nil {
150
+ return ov, nil
151
}
152
+ log.Debug(err)
153
56
- return "", fmt.Errorf("fuseversion: no system found")
154
+ return tryGFVFromFuseVersion()
155
}
156
157
func trySysctl() (string, error) {
@@ -65,3 +163,68 @@ func trySysctl() (string, error) {
163
log.Debug("mount: sysctl osxfuse.version.number:", v)
164
return v, nil
165
}
166
+
167
+func tryGFVFromFuseVersion() (string, error) {
168
+ if err := ensureFuseVersionIsInstalled(); err != nil {
169
+ return "", err
170
+ }
171
+
172
+ cmd := exec.Command("fuse-version", "-q", "-only", "agent", "-s", "OSXFUSE")
173
+ var out bytes.Buffer
174
+ cmd.Stdout = &out
175
+ if err := cmd.Run(); err != nil {
176
+ return "", fmt.Errorf(errStrFailedToRunFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, err)
177
+ }
178
+
179
+ return out.String(), nil
180
+}
181
+
182
+func ensureFuseVersionIsInstalled() error {
183
+ // see if fuse-version is there
184
+ if _, err := exec.LookPath("fuse-version"); err == nil {
185
+ return nil // got it!
186
+ }
187
+
188
+ // try installing it...
189
+ log.Debug("fuse-version: no fuse-version. attempting to install.")
190
+ cmd := exec.Command("go", "get", "github.com/jbenet/go-fuse-version/fuse-version")
191
+ var cmdout bytes.Buffer
192
+ cmd.Stdout = &cmdout
193
+ cmd.Stderr = &cmdout
194
+ if err := cmd.Run(); err != nil {
195
+ // Ok, install fuse-version failed. is it they dont have fuse?
196
+ cmdoutstr := cmdout.String()
197
+ if strings.Contains(cmdoutstr, errStrNoFuseHeaders) {
198
+ // yes! it is! they dont have fuse!
199
+ return fmt.Errorf(errStrFuseRequired)
200
+ }
201
+
202
+ log.Debug("fuse-version: failed to install.")
203
+ s := err.Error() + "\n" + cmdoutstr
204
+ return fmt.Errorf(errStrNeedFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, s)
205
+ }
206
+
207
+ // ok, try again...
208
+ if _, err := exec.LookPath("fuse-version"); err != nil {
209
+ log.Debug("fuse-version: failed to install?")
210
+ return fmt.Errorf(errStrNeedFuseVersion, fuseVersionPkg, dontCheckOSXFUSEConfigKey, err)
211
+ }
212
+
213
+ log.Debug("fuse-version: install success")
214
+ return nil
215
+}
216
+
217
+func userAskedToSkipFuseCheck(node *core.IpfsNode) (skip bool, err error) {
218
+ val, err := node.Repo.GetConfigKey(dontCheckOSXFUSEConfigKey)
219
+ if err != nil {
220
+ return false, nil // failed to get config value. dont skip check.
221
+ }
222
+ s, ok := val.(string)
223
+ if !ok {
224
+ // got config value, but it's invalid... dont skip check, ask the user to fix it...
225
+ return false, fmt.Errorf(errStrFixConfig, dontCheckOSXFUSEConfigKey, val,
226
+ dontCheckOSXFUSEConfigKey)
227
+ }
228
+ // only "true" counts as telling us to skip.
229
+ return s == "true", nil
230
+}
core/commands/mount_unix.go
+7
-5
@@ -23,6 +23,12 @@ const mountTimeout = time.Second
23
// fuseNoDirectory used to check the returning fuse error
24
const fuseNoDirectory = "fusermount: failed to access mountpoint"
25
26
+// platformFuseChecks can get overridden by arch-specific files
27
+// to run fuse checks (like checking the OSXFUSE version)
28
+var platformFuseChecks = func(*core.IpfsNode) error {
29
+ return nil
30
+}
31
+
32
var MountCmd = &cmds.Command{
33
Helptext: cmds.HelpText{
34
Tagline: "Mounts IPFS to the filesystem (read-only)",
@@ -161,7 +167,7 @@ func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
167
node.Mounts.Ipns.Unmount()
168
}
169
164
- if err := platformFuseChecks(); err != nil {
170
+ if err := platformFuseChecks(node); err != nil {
171
return err
172
}
173
@@ -173,10 +179,6 @@ func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
179
return nil
180
}
181
176
-var platformFuseChecks = func() error {
177
- return nil
178
-}
179
-
182
func doMount(node *core.IpfsNode, fsdir, nsdir string) error {
183
fmtFuseErr := func(err error) error {
184
s := err.Error()