Mounts detect unmounts and track mount state.
This lets FUSE mounts to track whether they are active or not by tracking when fs.Serve terminates. License: MIT Signed-off-by: Stephen Whitmore <noffle@ipfs.io>
Stephen Whitmore committed
Feb 4, 2016 at 12:55 UTC
40a49c8399b8e952c6852909f55ba5eea8d4d591
13 files changed
+296
-243
cmd/ipfs/daemon.go
+2
-1
@@ -21,6 +21,7 @@ import (
21
corehttp "github.com/ipfs/go-ipfs/core/corehttp"
22
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
23
"github.com/ipfs/go-ipfs/core/corerouting"
24
+ nodeMount "github.com/ipfs/go-ipfs/fuse/node"
25
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
26
util "github.com/ipfs/go-ipfs/util"
27
conn "gx/ipfs/QmUBogf4nUefBjmYjn6jfsfPJRkmDGSeMhNj4usRKq69f4/go-libp2p/p2p/net/conn"
@@ -494,7 +495,7 @@ func mountFuse(req cmds.Request) error {
495
return fmt.Errorf("mountFuse: ConstructNode() failed: %s", err)
496
}
497
497
- err = commands.Mount(node, fsdir, nsdir)
498
+ err = nodeMount.Mount(node, fsdir, nsdir)
499
if err != nil {
500
return err
501
}
core/commands/mount_nofuse.go
-7
@@ -4,10 +4,7 @@
4
package commands
5
6
import (
7
- "errors"
8
-
7
cmds "github.com/ipfs/go-ipfs/commands"
10
- "github.com/ipfs/go-ipfs/core"
8
)
9
10
var MountCmd = &cmds.Command{
@@ -23,7 +20,3 @@ For the latest instructions, please check the project's repository:
20
`,
21
},
22
}
26
-
27
-func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
28
- return errors.New("not compiled in")
29
-}
core/commands/mount_unix.go
+2
-109
@@ -7,32 +7,12 @@ import (
7
"fmt"
8
"io"
9
"strings"
10
- "time"
10
11
cmds "github.com/ipfs/go-ipfs/commands"
13
- core "github.com/ipfs/go-ipfs/core"
14
- ipns "github.com/ipfs/go-ipfs/fuse/ipns"
15
- mount "github.com/ipfs/go-ipfs/fuse/mount"
16
- rofs "github.com/ipfs/go-ipfs/fuse/readonly"
12
+ nodeMount "github.com/ipfs/go-ipfs/fuse/node"
13
config "github.com/ipfs/go-ipfs/repo/config"
14
)
15
20
-// amount of time to wait for mount errors
21
-// TODO is this non-deterministic?
22
-const mountTimeout = time.Second
23
-
24
-// fuseNoDirectory used to check the returning fuse error
25
-const fuseNoDirectory = "fusermount: failed to access mountpoint"
26
-
27
-// fuseExitStatus1 used to check the returning fuse error
28
-const fuseExitStatus1 = "fusermount: exit status 1"
29
-
30
-// platformFuseChecks can get overridden by arch-specific files
31
-// to run fuse checks (like checking the OSXFUSE version)
32
-var platformFuseChecks = func(*core.IpfsNode) error {
33
- return nil
34
-}
35
-
16
var MountCmd = &cmds.Command{
17
Helptext: cmds.HelpText{
18
Tagline: "Mounts IPFS to the filesystem (read-only).",
@@ -134,7 +114,7 @@ baz
114
nsdir = cfg.Mounts.IPNS // NB: be sure to not redeclare!
115
}
116
137
- err = Mount(node, fsdir, nsdir)
117
+ err = nodeMount.Mount(node, fsdir, nsdir)
118
if err != nil {
119
res.SetError(err, cmds.ErrNormal)
120
return
@@ -155,90 +135,3 @@ baz
135
},
136
},
137
}
158
-
159
-func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
160
- // check if we already have live mounts.
161
- // if the user said "Mount", then there must be something wrong.
162
- // so, close them and try again.
163
- if node.Mounts.Ipfs != nil {
164
- node.Mounts.Ipfs.Unmount()
165
- }
166
- if node.Mounts.Ipns != nil {
167
- node.Mounts.Ipns.Unmount()
168
- }
169
-
170
- if err := platformFuseChecks(node); err != nil {
171
- return err
172
- }
173
-
174
- var err error
175
- if err = doMount(node, fsdir, nsdir); err != nil {
176
- return err
177
- }
178
-
179
- return nil
180
-}
181
-
182
-func doMount(node *core.IpfsNode, fsdir, nsdir string) error {
183
- fmtFuseErr := func(err error, mountpoint string) error {
184
- s := err.Error()
185
- if strings.Contains(s, fuseNoDirectory) {
186
- s = strings.Replace(s, `fusermount: "fusermount:`, "", -1)
187
- s = strings.Replace(s, `\n", exit status 1`, "", -1)
188
- return cmds.ClientError(s)
189
- }
190
- if s == fuseExitStatus1 {
191
- s = fmt.Sprintf("fuse failed to access mountpoint %s", mountpoint)
192
- return cmds.ClientError(s)
193
- }
194
- return err
195
- }
196
-
197
- // this sync stuff is so that both can be mounted simultaneously.
198
- var fsmount mount.Mount
199
- var nsmount mount.Mount
200
- var err1 error
201
- var err2 error
202
-
203
- done := make(chan struct{})
204
-
205
- go func() {
206
- fsmount, err1 = rofs.Mount(node, fsdir)
207
- done <- struct{}{}
208
- }()
209
-
210
- go func() {
211
- nsmount, err2 = ipns.Mount(node, nsdir, fsdir)
212
- done <- struct{}{}
213
- }()
214
-
215
- <-done
216
- <-done
217
-
218
- if err1 != nil {
219
- log.Errorf("error mounting: %s", err1)
220
- }
221
-
222
- if err2 != nil {
223
- log.Errorf("error mounting: %s", err2)
224
- }
225
-
226
- if err1 != nil || err2 != nil {
227
- if fsmount != nil {
228
- fsmount.Unmount()
229
- }
230
- if nsmount != nil {
231
- nsmount.Unmount()
232
- }
233
-
234
- if err1 != nil {
235
- return fmtFuseErr(err1, fsdir)
236
- }
237
- return fmtFuseErr(err2, nsdir)
238
- }
239
-
240
- // setup node state, so that it can be cancelled
241
- node.Mounts.Ipfs = fsmount
242
- node.Mounts.Ipns = nsmount
243
- return nil
244
-}
core/commands/mount_windows.go
-7
@@ -4,7 +4,6 @@ import (
4
"errors"
5
6
cmds "github.com/ipfs/go-ipfs/commands"
7
- "github.com/ipfs/go-ipfs/core"
7
)
8
9
var MountCmd = &cmds.Command{
@@ -17,9 +16,3 @@ var MountCmd = &cmds.Command{
16
res.SetError(errors.New("Mount isn't compatible with Windows yet"), cmds.ErrNormal)
17
},
18
}
20
-
21
-func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
22
- // TODO
23
- // currently a no-op, but we don't want to return an error
24
- return nil
25
-}
core/core.go
+2
-2
@@ -335,10 +335,10 @@ func (n *IpfsNode) teardown() error {
335
closers = append(closers, n.Exchange)
336
}
337
338
- if n.Mounts.Ipfs != nil {
338
+ if n.Mounts.Ipfs != nil && !n.Mounts.Ipfs.IsActive() {
339
closers = append(closers, mount.Closer(n.Mounts.Ipfs))
340
}
341
- if n.Mounts.Ipns != nil {
341
+ if n.Mounts.Ipns != nil && !n.Mounts.Ipns.IsActive() {
342
closers = append(closers, mount.Closer(n.Mounts.Ipns))
343
}
344
fuse/ipns/ipns_test.go
+1
-107
@@ -15,12 +15,11 @@ import (
15
racedet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-detect-race"
16
17
core "github.com/ipfs/go-ipfs/core"
18
- context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
19
- //mfs "github.com/ipfs/go-ipfs/mfs"
18
namesys "github.com/ipfs/go-ipfs/namesys"
19
offroute "github.com/ipfs/go-ipfs/routing/offline"
20
u "github.com/ipfs/go-ipfs/util"
21
ci "github.com/ipfs/go-ipfs/util/testutil/ci"
22
+ context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
23
)
24
25
func maybeSkipFuseTests(t *testing.T) {
@@ -437,111 +436,6 @@ func TestFSThrash(t *testing.T) {
436
}
437
}
438
440
-/*
441
-func TestFastRepublish(t *testing.T) {
442
- if testing.Short() {
443
- t.SkipNow()
444
- }
445
-
446
- // make timeout noticeable.
447
- osrt := shortRepublishTimeout
448
- shortRepublishTimeout = time.Millisecond * 100
449
-
450
- olrt := longRepublishTimeout
451
- longRepublishTimeout = time.Second
452
-
453
- node, mnt := setupIpnsTest(t, nil)
454
-
455
- h, err := node.PrivateKey.GetPublic().Hash()
456
- if err != nil {
457
- t.Fatal(err)
458
- }
459
- pubkeyPath := "/ipns/" + u.Key(h).String()
460
-
461
- // set them back
462
- defer func() {
463
- shortRepublishTimeout = osrt
464
- longRepublishTimeout = olrt
465
- mnt.Close()
466
- }()
467
-
468
- closed := make(chan struct{})
469
- dataA := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
470
- dataB := []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
471
-
472
- fname := mnt.Dir + "/local/file"
473
-
474
- // get first resolved hash
475
- log.Debug("publishing first hash")
476
- writeFileData(t, dataA, fname) // random
477
- <-time.After(shortRepublishTimeout * 2)
478
- log.Debug("resolving first hash")
479
- resolvedHash, err := node.Namesys.Resolve(context.Background(), pubkeyPath)
480
- if err != nil {
481
- t.Fatal("resolve err:", pubkeyPath, err)
482
- }
483
-
484
- // constantly keep writing to the file
485
- go func(timeout time.Duration) {
486
- for {
487
- select {
488
- case <-closed:
489
- return
490
-
491
- case <-time.After(timeout * 8 / 10):
492
- writeFileData(t, dataB, fname)
493
- }
494
- }
495
- }(shortRepublishTimeout)
496
-
497
- hasPublished := func() bool {
498
- res, err := node.Namesys.Resolve(context.Background(), pubkeyPath)
499
- if err != nil {
500
- t.Fatalf("resolve err: %v", err)
501
- }
502
- return res != resolvedHash
503
- }
504
-
505
- // test things
506
-
507
- // at this point, should not have written dataA and not have written dataB
508
- rbuf, err := ioutil.ReadFile(fname)
509
- if err != nil || !bytes.Equal(rbuf, dataA) {
510
- t.Fatalf("Data inconsistent! %v %v", err, string(rbuf))
511
- }
512
-
513
- if hasPublished() {
514
- t.Fatal("published (wrote)")
515
- }
516
-
517
- <-time.After(shortRepublishTimeout * 11 / 10)
518
-
519
- // at this point, should have written written dataB, but not published it
520
- rbuf, err = ioutil.ReadFile(fname)
521
- if err != nil || !bytes.Equal(rbuf, dataB) {
522
- t.Fatalf("Data inconsistent! %v %v", err, string(rbuf))
523
- }
524
-
525
- if hasPublished() {
526
- t.Fatal("published (wrote)")
527
- }
528
-
529
- <-time.After(longRepublishTimeout * 11 / 10)
530
-
531
- // at this point, should have written written dataB, and published it
532
- rbuf, err = ioutil.ReadFile(fname)
533
- if err != nil || !bytes.Equal(rbuf, dataB) {
534
- t.Fatalf("Data inconsistent! %v %v", err, string(rbuf))
535
- }
536
-
537
- if !hasPublished() {
538
- t.Fatal("not published")
539
- }
540
-
541
- close(closed)
542
-}
543
-*/
544
-
439
// Test writing a medium sized file one byte at a time
440
func TestMultiWrite(t *testing.T) {
441
fuse/mount/fuse.go
+40
-9
@@ -4,7 +4,9 @@
4
package mount
5
6
import (
7
+ "errors"
8
"fmt"
9
+ "sync"
10
"time"
11
12
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
@@ -12,12 +14,16 @@ import (
14
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
15
)
16
17
+var ErrNotMounted = errors.New("not mounted")
18
+
19
// mount implements go-ipfs/fuse/mount
20
type mount struct {
21
mpoint string
22
filesys fs.FS
23
fuseConn *fuse.Conn
20
- // closeErr error
24
+
25
+ active bool
26
+ activeLock *sync.RWMutex
27
28
proc goprocess.Process
29
}
@@ -39,10 +45,12 @@ func NewMount(p goprocess.Process, fsys fs.FS, mountpoint string, allow_other bo
45
}
46
47
m := &mount{
42
- mpoint: mountpoint,
43
- fuseConn: conn,
44
- filesys: fsys,
45
- proc: goprocess.WithParent(p), // link it to parent.
48
+ mpoint: mountpoint,
49
+ fuseConn: conn,
50
+ filesys: fsys,
51
+ active: false,
52
+ activeLock: &sync.RWMutex{},
53
+ proc: goprocess.WithParent(p), // link it to parent.
54
}
55
m.proc.SetTeardown(m.unmount)
56
@@ -60,11 +68,14 @@ func (m *mount) mount() error {
68
69
errs := make(chan error, 1)
70
go func() {
71
+ // fs.Serve blocks until the filesystem is unmounted.
72
err := fs.Serve(m.fuseConn, m.filesys)
64
- log.Debugf("Mounting %s -- fs.Serve returned (%s)", err)
73
+ log.Debugf("%s is unmounted", m.MountPoint())
74
if err != nil {
75
+ log.Debugf("fs.Serve returned (%s)", err)
76
errs <- err
77
}
78
+ m.setActive(false)
79
}()
80
81
// wait for the mount process to be done, or timed out.
@@ -81,6 +92,8 @@ func (m *mount) mount() error {
92
return err
93
}
94
95
+ m.setActive(true)
96
+
97
log.Infof("Mounted %s", m.MountPoint())
98
return nil
99
}
@@ -95,6 +108,7 @@ func (m *mount) unmount() error {
108
// try unmounting with fuse lib
109
err := fuse.Unmount(m.MountPoint())
110
if err == nil {
111
+ m.setActive(false)
112
return nil
113
}
114
log.Warningf("fuse unmount err: %s", err)
@@ -102,11 +116,10 @@ func (m *mount) unmount() error {
116
// try closing the fuseConn
117
err = m.fuseConn.Close()
118
if err == nil {
119
+ m.setActive(false)
120
return nil
121
}
107
- if err != nil {
108
- log.Warningf("fuse conn error: %s", err)
109
- }
122
+ log.Warningf("fuse conn error: %s", err)
123
124
// try mount.ForceUnmountManyTimes
125
if err := ForceUnmountManyTimes(m, 10); err != nil {
@@ -114,6 +127,7 @@ func (m *mount) unmount() error {
127
}
128
129
log.Infof("Seemingly unmounted %s", m.MountPoint())
130
+ m.setActive(false)
131
return nil
132
}
133
@@ -126,6 +140,23 @@ func (m *mount) MountPoint() string {
140
}
141
142
func (m *mount) Unmount() error {
143
+ if !m.IsActive() {
144
+ return ErrNotMounted
145
+ }
146
+
147
// call Process Close(), which calls unmount() exactly once.
148
return m.proc.Close()
149
}
150
+
151
+func (m *mount) IsActive() bool {
152
+ m.activeLock.RLock()
153
+ defer m.activeLock.RUnlock()
154
+
155
+ return m.active
156
+}
157
+
158
+func (m *mount) setActive(a bool) {
159
+ m.activeLock.Lock()
160
+ m.active = a
161
+ m.activeLock.Unlock()
162
+}
fuse/mount/mount.go
+3
@@ -25,6 +25,9 @@ type Mount interface {
25
// Unmounts the mount
26
Unmount() error
27
28
+ // Checks if the mount is still active.
29
+ IsActive() bool
30
+
31
// Process returns the mount's Process to be able to link it
32
// to other processes. Unmount upon closing.
33
Process() goprocess.Process
fuse/node/mount_darwin.go
renamed
+1
-1
@@ -1,6 +1,6 @@
1
// +build !nofuse
2
3
-package commands
3
+package node
4
5
import (
6
"bytes"
fuse/node/mount_nofuse.go
new
+14
@@ -0,0 +1,14 @@
1
+// +build linux darwin freebsd
2
+// +build nofuse
3
+
4
+package node
5
+
6
+import (
7
+ "errors"
8
+
9
+ core "github.com/ipfs/go-ipfs/core"
10
+)
11
+
12
+func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
13
+ return errors.New("not compiled in")
14
+}
fuse/node/mount_test.go
new
+98
@@ -0,0 +1,98 @@
1
+// +build !nofuse
2
+
3
+package node
4
+
5
+import (
6
+ "io/ioutil"
7
+ "os"
8
+ "os/exec"
9
+ "testing"
10
+ "time"
11
+
12
+ context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
13
+ core "github.com/ipfs/go-ipfs/core"
14
+ ipns "github.com/ipfs/go-ipfs/fuse/ipns"
15
+ mount "github.com/ipfs/go-ipfs/fuse/mount"
16
+ namesys "github.com/ipfs/go-ipfs/namesys"
17
+ offroute "github.com/ipfs/go-ipfs/routing/offline"
18
+ ci "github.com/ipfs/go-ipfs/util/testutil/ci"
19
+)
20
+
21
+func maybeSkipFuseTests(t *testing.T) {
22
+ if ci.NoFuse() {
23
+ t.Skip("Skipping FUSE tests")
24
+ }
25
+}
26
+
27
+func mkdir(t *testing.T, path string) {
28
+ err := os.Mkdir(path, os.ModeDir|os.ModePerm)
29
+ if err != nil {
30
+ t.Fatal(err)
31
+ }
32
+}
33
+
34
+// Test externally unmounting, then trying to unmount in code
35
+func TestExternalUnmount(t *testing.T) {
36
+ if testing.Short() {
37
+ t.SkipNow()
38
+ }
39
+
40
+ // TODO: needed?
41
+ maybeSkipFuseTests(t)
42
+
43
+ node, err := core.NewNode(context.Background(), nil)
44
+ if err != nil {
45
+ t.Fatal(err)
46
+ }
47
+
48
+ err = node.LoadPrivateKey()
49
+ if err != nil {
50
+ t.Fatal(err)
51
+ }
52
+
53
+ node.Routing = offroute.NewOfflineRouter(node.Repo.Datastore(), node.PrivateKey)
54
+ node.Namesys = namesys.NewNameSystem(node.Routing, node.Repo.Datastore(), 0)
55
+
56
+ err = ipns.InitializeKeyspace(node, node.PrivateKey)
57
+ if err != nil {
58
+ t.Fatal(err)
59
+ }
60
+
61
+ // get the test dir paths (/tmp/fusetestXXXX)
62
+ dir, err := ioutil.TempDir("", "fusetest")
63
+ if err != nil {
64
+ t.Fatal(err)
65
+ }
66
+
67
+ ipfsDir := dir + "/ipfs"
68
+ ipnsDir := dir + "/ipns"
69
+ mkdir(t, ipfsDir)
70
+ mkdir(t, ipnsDir)
71
+
72
+ err = Mount(node, ipfsDir, ipnsDir)
73
+ if err != nil {
74
+ t.Fatal(err)
75
+ }
76
+
77
+ // Run shell command to externally unmount the directory
78
+ cmd := "fusermount"
79
+ args := []string{"-u", ipnsDir}
80
+ if err := exec.Command(cmd, args...).Run(); err != nil {
81
+ t.Fatal(err)
82
+ }
83
+
84
+ // TODO(noffle): it takes a moment for the goroutine that's running fs.Serve to be notified and do its cleanup.
85
+ time.Sleep(time.Millisecond * 100)
86
+
87
+ // Attempt to unmount IPNS; check that it was already unmounted.
88
+ err = node.Mounts.Ipns.Unmount()
89
+ if err != mount.ErrNotMounted {
90
+ t.Fatal("Unmount should have failed")
91
+ }
92
+
93
+ // Attempt to unmount IPFS; it should unmount successfully.
94
+ err = node.Mounts.Ipfs.Unmount()
95
+ if err != nil {
96
+ t.Fatal(err)
97
+ }
98
+}
fuse/node/mount_unix.go
new
+122
@@ -0,0 +1,122 @@
1
+// +build linux darwin freebsd
2
+// +build !nofuse
3
+
4
+package node
5
+
6
+import (
7
+ "errors"
8
+ "fmt"
9
+ "strings"
10
+ "time"
11
+
12
+ core "github.com/ipfs/go-ipfs/core"
13
+ ipns "github.com/ipfs/go-ipfs/fuse/ipns"
14
+ mount "github.com/ipfs/go-ipfs/fuse/mount"
15
+ rofs "github.com/ipfs/go-ipfs/fuse/readonly"
16
+ logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
17
+)
18
+
19
+var log = logging.Logger("node")
20
+
21
+// amount of time to wait for mount errors
22
+// TODO is this non-deterministic?
23
+const mountTimeout = time.Second
24
+
25
+// fuseNoDirectory used to check the returning fuse error
26
+const fuseNoDirectory = "fusermount: failed to access mountpoint"
27
+
28
+// fuseExitStatus1 used to check the returning fuse error
29
+const fuseExitStatus1 = "fusermount: exit status 1"
30
+
31
+// platformFuseChecks can get overridden by arch-specific files
32
+// to run fuse checks (like checking the OSXFUSE version)
33
+var platformFuseChecks = func(*core.IpfsNode) error {
34
+ return nil
35
+}
36
+
37
+func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
38
+ // check if we already have live mounts.
39
+ // if the user said "Mount", then there must be something wrong.
40
+ // so, close them and try again.
41
+ if node.Mounts.Ipfs != nil && node.Mounts.Ipfs.IsActive() {
42
+ node.Mounts.Ipfs.Unmount()
43
+ }
44
+ if node.Mounts.Ipns != nil && node.Mounts.Ipns.IsActive() {
45
+ node.Mounts.Ipns.Unmount()
46
+ }
47
+
48
+ if err := platformFuseChecks(node); err != nil {
49
+ return err
50
+ }
51
+
52
+ var err error
53
+ if err = doMount(node, fsdir, nsdir); err != nil {
54
+ return err
55
+ }
56
+
57
+ return nil
58
+}
59
+
60
+func doMount(node *core.IpfsNode, fsdir, nsdir string) error {
61
+ fmtFuseErr := func(err error, mountpoint string) error {
62
+ s := err.Error()
63
+ if strings.Contains(s, fuseNoDirectory) {
64
+ s = strings.Replace(s, `fusermount: "fusermount:`, "", -1)
65
+ s = strings.Replace(s, `\n", exit status 1`, "", -1)
66
+ return errors.New(s)
67
+ }
68
+ if s == fuseExitStatus1 {
69
+ s = fmt.Sprintf("fuse failed to access mountpoint %s", mountpoint)
70
+ return errors.New(s)
71
+ }
72
+ return err
73
+ }
74
+
75
+ // this sync stuff is so that both can be mounted simultaneously.
76
+ var fsmount mount.Mount
77
+ var nsmount mount.Mount
78
+ var err1 error
79
+ var err2 error
80
+
81
+ done := make(chan struct{})
82
+
83
+ go func() {
84
+ fsmount, err1 = rofs.Mount(node, fsdir)
85
+ done <- struct{}{}
86
+ }()
87
+
88
+ go func() {
89
+ nsmount, err2 = ipns.Mount(node, nsdir, fsdir)
90
+ done <- struct{}{}
91
+ }()
92
+
93
+ <-done
94
+ <-done
95
+
96
+ if err1 != nil {
97
+ log.Errorf("error mounting: %s", err1)
98
+ }
99
+
100
+ if err2 != nil {
101
+ log.Errorf("error mounting: %s", err2)
102
+ }
103
+
104
+ if err1 != nil || err2 != nil {
105
+ if fsmount != nil {
106
+ fsmount.Unmount()
107
+ }
108
+ if nsmount != nil {
109
+ nsmount.Unmount()
110
+ }
111
+
112
+ if err1 != nil {
113
+ return fmtFuseErr(err1, fsdir)
114
+ }
115
+ return fmtFuseErr(err2, nsdir)
116
+ }
117
+
118
+ // setup node state, so that it can be cancelled
119
+ node.Mounts.Ipfs = fsmount
120
+ node.Mounts.Ipns = nsmount
121
+ return nil
122
+}
fuse/node/mount_windows.go
new
+11
@@ -0,0 +1,11 @@
1
+package node
2
+
3
+import (
4
+ "github.com/ipfs/go-ipfs/core"
5
+)
6
+
7
+func Mount(node *core.IpfsNode, fsdir, nsdir string) error {
8
+ // TODO
9
+ // currently a no-op, but we don't want to return an error
10
+ return nil
11
+}