refactor ipns fuse to utilize ipnsfs
Jeromy committed
Mar 16, 2015 at 17:25 UTC
b9658f0cb259b6b12fe64444899b48918028c2e8
3 files changed
+433
-464
fuse/ipns/ipns_test.go
+226
-15
@@ -5,16 +5,19 @@ package ipns
5
import (
6
"bytes"
7
"crypto/rand"
8
+ "fmt"
9
"io/ioutil"
10
+ mrand "math/rand"
11
"os"
12
+ "sync"
13
"testing"
11
- "time"
14
15
fstest "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs/fstestutil"
14
- context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
16
+ racedet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-detect-race"
17
18
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
19
core "github.com/jbenet/go-ipfs/core"
17
- u "github.com/jbenet/go-ipfs/util"
20
+ nsfs "github.com/jbenet/go-ipfs/ipnsfs"
21
ci "github.com/jbenet/go-ipfs/util/testutil/ci"
22
)
23
@@ -30,6 +33,13 @@ func randBytes(size int) []byte {
33
return b
34
}
35
36
+func mkdir(t *testing.T, path string) {
37
+ err := os.Mkdir(path, os.ModeDir)
38
+ if err != nil {
39
+ t.Fatal(err)
40
+ }
41
+}
42
+
43
func writeFile(t *testing.T, size int, path string) []byte {
44
return writeFileData(t, randBytes(size), path)
45
}
@@ -57,6 +67,38 @@ func writeFileData(t *testing.T, data []byte, path string) []byte {
67
return data
68
}
69
70
+func verifyFile(t *testing.T, path string, data []byte) {
71
+ fi, err := os.Open(path)
72
+ if err != nil {
73
+ t.Fatal(err)
74
+ }
75
+ defer fi.Close()
76
+
77
+ out, err := ioutil.ReadAll(fi)
78
+ if err != nil {
79
+ t.Fatal(err)
80
+ }
81
+
82
+ if !bytes.Equal(out, data) {
83
+ t.Fatal("Data not equal")
84
+ }
85
+}
86
+
87
+func checkExists(t *testing.T, path string) {
88
+ _, err := os.Stat(path)
89
+ if err != nil {
90
+ t.Fatal(err)
91
+ }
92
+}
93
+
94
+func closeMount(mnt *fstest.Mount) {
95
+ if err := recover(); err != nil {
96
+ log.Error("Recovered panic")
97
+ log.Error(err)
98
+ }
99
+ mnt.Close()
100
+}
101
+
102
func setupIpnsTest(t *testing.T, node *core.IpfsNode) (*core.IpfsNode, *fstest.Mount) {
103
maybeSkipFuseTests(t)
104
@@ -66,6 +108,13 @@ func setupIpnsTest(t *testing.T, node *core.IpfsNode) (*core.IpfsNode, *fstest.M
108
if err != nil {
109
t.Fatal(err)
110
}
111
+
112
+ ipnsfs, err := nsfs.NewFilesystem(context.TODO(), node.DAG, node.Namesys, node.Pinning, node.PrivateKey)
113
+ if err != nil {
114
+ t.Fatal(err)
115
+ }
116
+
117
+ node.IpnsFs = ipnsfs
118
}
119
120
fs, err := NewFileSystem(node, node.PrivateKey, "")
@@ -80,17 +129,29 @@ func setupIpnsTest(t *testing.T, node *core.IpfsNode) (*core.IpfsNode, *fstest.M
129
return node, mnt
130
}
131
132
+func TestIpnsLocalLink(t *testing.T) {
133
+ _, mnt := setupIpnsTest(t, nil)
134
+ defer mnt.Close()
135
+ name := mnt.Dir + "/local"
136
+
137
+ finfo, err := os.Stat(name)
138
+ if err != nil {
139
+ t.Fatal(err)
140
+ }
141
+
142
+ t.Log(finfo.Name())
143
+}
144
+
145
// Test writing a file and reading it back
146
func TestIpnsBasicIO(t *testing.T) {
85
- t.Skip("Skipping until DAGModifier can be fixed.")
147
if testing.Short() {
148
t.SkipNow()
149
}
150
_, mnt := setupIpnsTest(t, nil)
90
- defer mnt.Close()
151
+ defer closeMount(mnt)
152
153
fname := mnt.Dir + "/local/testfile"
93
- data := writeFile(t, 12345, fname)
154
+ data := writeFile(t, 10, fname)
155
156
rbuf, err := ioutil.ReadFile(fname)
157
if err != nil {
@@ -104,7 +165,6 @@ func TestIpnsBasicIO(t *testing.T) {
165
166
// Test to make sure file changes persist over mounts of ipns
167
func TestFilePersistence(t *testing.T) {
107
- t.Skip("Skipping until DAGModifier can be fixed.")
168
if testing.Short() {
169
t.SkipNow()
170
}
@@ -113,11 +173,9 @@ func TestFilePersistence(t *testing.T) {
173
fname := "/local/atestfile"
174
data := writeFile(t, 127, mnt.Dir+fname)
175
116
- // Wait for publish: TODO: make publish happen faster in tests
117
- time.Sleep(time.Millisecond * 40)
118
-
176
mnt.Close()
177
178
+ t.Log("Closed, opening new fs")
179
node, mnt = setupIpnsTest(t, node)
180
defer mnt.Close()
181
@@ -131,9 +189,45 @@ func TestFilePersistence(t *testing.T) {
189
}
190
}
191
192
+func TestDeeperDirs(t *testing.T) {
193
+ node, mnt := setupIpnsTest(t, nil)
194
+
195
+ t.Log("make a top level dir")
196
+ dir1 := "/local/test1"
197
+ mkdir(t, mnt.Dir+dir1)
198
+
199
+ checkExists(t, mnt.Dir+dir1)
200
+
201
+ t.Log("write a file in it")
202
+ data1 := writeFile(t, 4000, mnt.Dir+dir1+"/file1")
203
+
204
+ verifyFile(t, mnt.Dir+dir1+"/file1", data1)
205
+
206
+ t.Log("sub directory")
207
+ mkdir(t, mnt.Dir+dir1+"/dir2")
208
+
209
+ checkExists(t, mnt.Dir+dir1+"/dir2")
210
+
211
+ t.Log("file in that subdirectory")
212
+ data2 := writeFile(t, 5000, mnt.Dir+dir1+"/dir2/file2")
213
+
214
+ verifyFile(t, mnt.Dir+dir1+"/dir2/file2", data2)
215
+
216
+ mnt.Close()
217
+ t.Log("closing mount, then restarting")
218
+
219
+ _, mnt = setupIpnsTest(t, node)
220
+
221
+ checkExists(t, mnt.Dir+dir1)
222
+
223
+ verifyFile(t, mnt.Dir+dir1+"/file1", data1)
224
+
225
+ verifyFile(t, mnt.Dir+dir1+"/dir2/file2", data2)
226
+ mnt.Close()
227
+}
228
+
229
// Test to make sure the filesystem reports file sizes correctly
230
func TestFileSizeReporting(t *testing.T) {
136
- t.Skip("Skipping until DAGModifier can be fixed.")
231
if testing.Short() {
232
t.SkipNow()
233
}
@@ -155,7 +249,6 @@ func TestFileSizeReporting(t *testing.T) {
249
250
// Test to make sure you cant create multiple entries with the same name
251
func TestDoubleEntryFailure(t *testing.T) {
158
- t.Skip("Skipping until DAGModifier can be fixed.")
252
if testing.Short() {
253
t.SkipNow()
254
}
@@ -175,7 +268,6 @@ func TestDoubleEntryFailure(t *testing.T) {
268
}
269
270
func TestAppendFile(t *testing.T) {
178
- t.Skip("Skipping until DAGModifier can be fixed.")
271
if testing.Short() {
272
t.SkipNow()
273
}
@@ -216,8 +308,126 @@ func TestAppendFile(t *testing.T) {
308
}
309
}
310
311
+func TestConcurrentWrites(t *testing.T) {
312
+ if testing.Short() {
313
+ t.SkipNow()
314
+ }
315
+ _, mnt := setupIpnsTest(t, nil)
316
+ defer mnt.Close()
317
+
318
+ nactors := 4
319
+ filesPerActor := 400
320
+ fileSize := 2000
321
+
322
+ data := make([][][]byte, nactors)
323
+
324
+ if racedet.WithRace() {
325
+ nactors = 2
326
+ filesPerActor = 50
327
+ }
328
+
329
+ wg := sync.WaitGroup{}
330
+ for i := 0; i < nactors; i++ {
331
+ data[i] = make([][]byte, filesPerActor)
332
+ wg.Add(1)
333
+ go func(n int) {
334
+ defer wg.Done()
335
+ for j := 0; j < filesPerActor; j++ {
336
+ out := writeFile(t, fileSize, mnt.Dir+fmt.Sprintf("/local/%dFILE%d", n, j))
337
+ data[n][j] = out
338
+ }
339
+ }(i)
340
+ }
341
+ wg.Wait()
342
+
343
+ for i := 0; i < nactors; i++ {
344
+ for j := 0; j < filesPerActor; j++ {
345
+ verifyFile(t, mnt.Dir+fmt.Sprintf("/local/%dFILE%d", i, j), data[i][j])
346
+ }
347
+ }
348
+}
349
+
350
+func TestFSThrash(t *testing.T) {
351
+ files := make(map[string][]byte)
352
+
353
+ if testing.Short() {
354
+ t.SkipNow()
355
+ }
356
+ _, mnt := setupIpnsTest(t, nil)
357
+ defer mnt.Close()
358
+
359
+ base := mnt.Dir + "/local"
360
+ dirs := []string{base}
361
+ dirlock := sync.RWMutex{}
362
+ filelock := sync.Mutex{}
363
+
364
+ ndirWorkers := 2
365
+ nfileWorkers := 2
366
+
367
+ ndirs := 100
368
+ nfiles := 200
369
+
370
+ wg := sync.WaitGroup{}
371
+
372
+ // Spawn off workers to make directories
373
+ for i := 0; i < ndirWorkers; i++ {
374
+ wg.Add(1)
375
+ go func(worker int) {
376
+ defer wg.Done()
377
+ for j := 0; j < ndirs; j++ {
378
+ dirlock.RLock()
379
+ n := mrand.Intn(len(dirs))
380
+ dir := dirs[n]
381
+ dirlock.RUnlock()
382
+
383
+ newDir := fmt.Sprintf("%s/dir%d-%d", dir, worker, j)
384
+ err := os.Mkdir(newDir, os.ModeDir)
385
+ if err != nil {
386
+ t.Fatal(err)
387
+ }
388
+ dirlock.Lock()
389
+ dirs = append(dirs, newDir)
390
+ dirlock.Unlock()
391
+ }
392
+ }(i)
393
+ }
394
+
395
+ // Spawn off workers to make files
396
+ for i := 0; i < nfileWorkers; i++ {
397
+ wg.Add(1)
398
+ go func(worker int) {
399
+ defer wg.Done()
400
+ for j := 0; j < nfiles; j++ {
401
+ dirlock.RLock()
402
+ n := mrand.Intn(len(dirs))
403
+ dir := dirs[n]
404
+ dirlock.RUnlock()
405
+
406
+ newFileName := fmt.Sprintf("%s/file%d-%d", dir, worker, j)
407
+
408
+ data := writeFile(t, 2000+mrand.Intn(5000), newFileName)
409
+ filelock.Lock()
410
+ files[newFileName] = data
411
+ filelock.Unlock()
412
+ }
413
+ }(i)
414
+ }
415
+
416
+ wg.Wait()
417
+ for name, data := range files {
418
+ out, err := ioutil.ReadFile(name)
419
+ if err != nil {
420
+ t.Fatal(err)
421
+ }
422
+
423
+ if !bytes.Equal(data, out) {
424
+ t.Fatal("Data didnt match")
425
+ }
426
+ }
427
+}
428
+
429
+/*
430
func TestFastRepublish(t *testing.T) {
220
- t.Skip("Skipping until DAGModifier can be fixed.")
431
if testing.Short() {
432
t.SkipNow()
433
}
@@ -319,10 +529,11 @@ func TestFastRepublish(t *testing.T) {
529
530
close(closed)
531
}
532
+*/
533
534
// Test writing a medium sized file one byte at a time
535
func TestMultiWrite(t *testing.T) {
325
- t.Skip("Skipping until DAGModifier can be fixed.")
536
+
537
if testing.Short() {
538
t.SkipNow()
539
}
fuse/ipns/ipns_unix.go
+207
-405
@@ -8,37 +8,22 @@ import (
8
"errors"
9
"io"
10
"os"
11
- "path/filepath"
12
- "time"
11
12
fuse "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
13
fs "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
16
- proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
14
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
15
eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog"
16
17
core "github.com/jbenet/go-ipfs/core"
21
- chunk "github.com/jbenet/go-ipfs/importer/chunk"
22
- mdag "github.com/jbenet/go-ipfs/merkledag"
18
+ nsfs "github.com/jbenet/go-ipfs/ipnsfs"
19
+ dag "github.com/jbenet/go-ipfs/merkledag"
20
ci "github.com/jbenet/go-ipfs/p2p/crypto"
24
- path "github.com/jbenet/go-ipfs/path"
21
ft "github.com/jbenet/go-ipfs/unixfs"
26
- uio "github.com/jbenet/go-ipfs/unixfs/io"
27
- mod "github.com/jbenet/go-ipfs/unixfs/mod"
28
- ftpb "github.com/jbenet/go-ipfs/unixfs/pb"
22
u "github.com/jbenet/go-ipfs/util"
30
- lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"
23
)
24
33
-const IpnsReadonly = true
34
-
25
var log = eventlog.Logger("fuse/ipns")
26
37
-var (
38
- shortRepublishTimeout = time.Millisecond * 5
39
- longRepublishTimeout = time.Millisecond * 500
40
-)
41
-
27
// FileSystem is the readwrite IPNS Fuse Filesystem.
28
type FileSystem struct {
29
Ipfs *core.IpfsNode
@@ -54,89 +39,73 @@ func NewFileSystem(ipfs *core.IpfsNode, sk ci.PrivKey, ipfspath string) (*FileSy
39
return &FileSystem{Ipfs: ipfs, RootNode: root}, nil
40
}
41
57
-func CreateRoot(n *core.IpfsNode, keys []ci.PrivKey, ipfsroot string) (*Root, error) {
58
- root := new(Root)
59
- root.LocalDirs = make(map[string]*Node)
60
- root.Ipfs = n
61
- abspath, err := filepath.Abs(ipfsroot)
42
+// Root constructs the Root of the filesystem, a Root object.
43
+func (f *FileSystem) Root() (fs.Node, error) {
44
+ log.Debug("Filesystem, get root")
45
+ return f.RootNode, nil
46
+}
47
+
48
+func (f *FileSystem) Destroy() {
49
+ err := f.RootNode.Close()
50
if err != nil {
63
- return nil, err
51
+ log.Errorf("Error Shutting Down Filesystem: %s\n", err)
52
}
65
- root.IpfsRoot = abspath
53
+}
54
+
55
+// Root is the root object of the filesystem tree.
56
+type Root struct {
57
+ Ipfs *core.IpfsNode
58
+ Keys []ci.PrivKey
59
67
- root.Keys = keys
60
+ // Used for symlinking into ipfs
61
+ IpfsRoot string
62
+ LocalDirs map[string]fs.Node
63
+ Roots map[string]*nsfs.KeyRoot
64
69
- if len(keys) == 0 {
70
- log.Warning("No keys given for ipns root creation")
71
- } else {
72
- k := keys[0]
73
- pub := k.GetPublic()
74
- hash, err := pub.Hash()
75
- if err != nil {
76
- return nil, err
77
- }
78
- root.LocalLink = &Link{u.Key(hash).Pretty()}
79
- }
65
+ fs *nsfs.Filesystem
66
+ LocalLink *Link
67
+}
68
69
+func CreateRoot(ipfs *core.IpfsNode, keys []ci.PrivKey, ipfspath string) (*Root, error) {
70
+ ldirs := make(map[string]fs.Node)
71
+ roots := make(map[string]*nsfs.KeyRoot)
72
for _, k := range keys {
82
- hash, err := k.GetPublic().Hash()
73
+ pkh, err := k.GetPublic().Hash()
74
if err != nil {
84
- log.Debug("failed to hash public key.")
85
- continue
75
+ return nil, err
76
}
87
- name := u.Key(hash).Pretty()
88
- nd := new(Node)
89
- nd.Ipfs = n
90
- nd.key = k
91
- nd.repub = NewRepublisher(nd, shortRepublishTimeout, longRepublishTimeout)
92
-
93
- go nd.repub.Run()
94
-
95
- pointsTo, err := n.Namesys.Resolve(n.Context(), name)
77
+ name := u.Key(pkh).B58String()
78
+ root, err := ipfs.IpnsFs.GetRoot(name)
79
if err != nil {
97
- log.Warning("Could not resolve value for local ipns entry, providing empty dir")
98
- nd.Nd = &mdag.Node{Data: ft.FolderPBData()}
99
- root.LocalDirs[name] = nd
100
- continue
80
+ return nil, err
81
}
82
103
- if !u.IsValidHash(pointsTo.B58String()) {
104
- log.Criticalf("Got back bad data from namesys resolve! [%s]", pointsTo)
105
- return nil, nil
106
- }
83
+ roots[name] = root
84
108
- node, err := n.Resolver.ResolvePath(path.Path(pointsTo.B58String()))
109
- if err != nil {
110
- log.Warning("Failed to resolve value from ipns entry in ipfs")
111
- continue
85
+ switch val := root.GetValue().(type) {
86
+ case *nsfs.Directory:
87
+ ldirs[name] = &Directory{dir: val}
88
+ case *nsfs.File:
89
+ ldirs[name] = &File{fi: val}
90
+ default:
91
+ return nil, errors.New("unrecognized type")
92
}
113
-
114
- nd.Nd = node
115
- root.LocalDirs[name] = nd
93
}
94
118
- return root, nil
119
-}
120
-
121
-// Root constructs the Root of the filesystem, a Root object.
122
-func (f FileSystem) Root() (fs.Node, error) {
123
- return f.RootNode, nil
124
-}
125
-
126
-// Root is the root object of the filesystem tree.
127
-type Root struct {
128
- Ipfs *core.IpfsNode
129
- Keys []ci.PrivKey
130
-
131
- // Used for symlinking into ipfs
132
- IpfsRoot string
133
- LocalDirs map[string]*Node
134
-
135
- LocalLink *Link
95
+ return &Root{
96
+ fs: ipfs.IpnsFs,
97
+ Ipfs: ipfs,
98
+ IpfsRoot: ipfspath,
99
+ Keys: keys,
100
+ LocalDirs: ldirs,
101
+ LocalLink: &Link{ipfs.Identity.Pretty()},
102
+ Roots: roots,
103
+ }, nil
104
}
105
106
// Attr returns file attributes.
107
func (*Root) Attr() fuse.Attr {
108
+ log.Debug("Root Attr")
109
return fuse.Attr{Mode: os.ModeDir | 0111} // -rw+x
110
}
111
@@ -148,6 +117,7 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) {
117
return nil, fuse.ENOENT
118
}
119
120
+ // Local symlink to the node ID keyspace
121
if name == "local" {
122
if s.LocalLink == nil {
123
return nil, fuse.ENOENT
@@ -157,9 +127,17 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) {
127
128
nd, ok := s.LocalDirs[name]
129
if ok {
160
- return nd, nil
130
+ switch nd := nd.(type) {
131
+ case *Directory:
132
+ return nd, nil
133
+ case *File:
134
+ return nd, nil
135
+ default:
136
+ return nil, fuse.EIO
137
+ }
138
}
139
140
+ // other links go through ipns resolution and are symlinked into the ipfs mountpoint
141
resolved, err := s.Ipfs.Namesys.Resolve(s.Ipfs.Context(), name)
142
if err != nil {
143
log.Warningf("ipns: namesys resolve error: %s", err)
@@ -169,8 +147,29 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) {
147
return &Link{s.IpfsRoot + "/" + resolved.B58String()}, nil
148
}
149
172
-// ReadDirAll reads a particular directory. Disallowed for root.
150
+func (r *Root) Close() error {
151
+ for _, kr := range r.Roots {
152
+ err := kr.Publish(r.Ipfs.Context())
153
+ if err != nil {
154
+ return err
155
+ }
156
+ }
157
+ return nil
158
+}
159
+
160
+// Forget is called when the filesystem is unmounted. probably.
161
+// see comments here: http://godoc.org/bazil.org/fuse/fs#FSDestroyer
162
+func (r *Root) Forget() {
163
+ err := r.Close()
164
+ if err != nil {
165
+ log.Error(err)
166
+ }
167
+}
168
+
169
+// ReadDirAll reads a particular directory. Will show locally available keys
170
+// as well as a symlink to the peerID key
171
func (r *Root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
172
+ log.Debug("Root ReadDirAll")
173
listing := []fuse.Dirent{
174
fuse.Dirent{
175
Name: "local",
@@ -192,115 +191,78 @@ func (r *Root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
191
return listing, nil
192
}
193
195
-// Node is the core object representing a filesystem tree node.
196
-type Node struct {
197
- root *Root
198
- nsRoot *Node
199
- parent *Node
200
-
201
- repub *Republisher
194
+// Directory is wrapper over an ipnsfs directory to satisfy the fuse fs interface
195
+type Directory struct {
196
+ dir *nsfs.Directory
197
203
- // This nodes name in its parent dir.
204
- // NOTE: this strategy wont work well if we allow hard links
205
- // (im all for murdering the thought of hard links)
206
- name string
198
+ fs.NodeRef
199
+}
200
208
- // Private keys held by nodes at the root of a keyspace
209
- // WARNING(security): the PrivKey interface is currently insecure
210
- // (holds the raw key). It will be secured later.
211
- key ci.PrivKey
201
+// File is wrapper over an ipnsfs file to satisfy the fuse fs interface
202
+type File struct {
203
+ fi *nsfs.File
204
213
- Ipfs *core.IpfsNode
214
- Nd *mdag.Node
215
- dagMod *mod.DagModifier
216
- cached *ftpb.Data
205
+ fs.NodeRef
206
}
207
219
-func (s *Node) loadData() error {
220
- s.cached = new(ftpb.Data)
221
- return proto.Unmarshal(s.Nd.Data, s.cached)
208
+// Attr returns the attributes of a given node.
209
+func (d *Directory) Attr() fuse.Attr {
210
+ log.Debug("Directory Attr")
211
+ return fuse.Attr{Mode: os.ModeDir | 0555}
212
}
213
214
// Attr returns the attributes of a given node.
225
-func (s *Node) Attr() fuse.Attr {
226
- if s.cached == nil {
227
- err := s.loadData()
228
- if err != nil {
229
- log.Debugf("Error loading PBData for file: '%s'", s.name)
230
- }
215
+func (fi *File) Attr() fuse.Attr {
216
+ log.Debug("File Attr")
217
+ size, err := fi.fi.Size()
218
+ if err != nil {
219
+ // In this case, the dag node in question may not be unixfs
220
+ log.Critical("Failed to get file size: %s", err)
221
}
232
- switch s.cached.GetType() {
233
- case ftpb.Data_Directory:
234
- return fuse.Attr{Mode: os.ModeDir | 0555}
235
- case ftpb.Data_File, ftpb.Data_Raw:
236
- size, err := ft.DataSize(s.Nd.Data)
237
- if err != nil {
238
- log.Debugf("Error getting size of file: %s", err)
239
- size = 0
240
- }
241
- if size == 0 {
242
- dmsize, err := s.dagMod.Size()
243
- if err != nil {
244
- log.Error(err)
245
- }
246
- size = uint64(dmsize)
247
- }
248
-
249
- mode := os.FileMode(0666)
250
- if IpnsReadonly {
251
- mode = 0444
252
- }
253
-
254
- return fuse.Attr{
255
- Mode: mode,
256
- Size: size,
257
- Blocks: uint64(len(s.Nd.Links)),
258
- }
259
- default:
260
- log.Debug("Invalid data type.")
261
- return fuse.Attr{}
222
+ return fuse.Attr{
223
+ Mode: os.FileMode(0666),
224
+ Size: uint64(size),
225
}
226
}
227
228
// Lookup performs a lookup under this node.
266
-func (s *Node) Lookup(ctx context.Context, name string) (fs.Node, error) {
267
- nodes, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
229
+func (s *Directory) Lookup(ctx context.Context, name string) (fs.Node, error) {
230
+ child, err := s.dir.Child(name)
231
if err != nil {
232
// todo: make this error more versatile.
233
return nil, fuse.ENOENT
234
}
235
273
- return s.makeChild(name, nodes[len(nodes)-1]), nil
274
-}
275
-
276
-func (n *Node) makeChild(name string, node *mdag.Node) *Node {
277
- child := &Node{
278
- Ipfs: n.Ipfs,
279
- Nd: node,
280
- name: name,
281
- nsRoot: n.nsRoot,
282
- parent: n,
283
- }
284
-
285
- // Always ensure that each child knows where the root is
286
- if n.nsRoot == nil {
287
- child.nsRoot = n
288
- } else {
289
- child.nsRoot = n.nsRoot
236
+ switch child := child.(type) {
237
+ case *nsfs.Directory:
238
+ return &Directory{dir: child}, nil
239
+ case *nsfs.File:
240
+ return &File{fi: child}, nil
241
+ default:
242
+ panic("system has proven to be insane")
243
}
291
-
292
- return child
244
}
245
246
// ReadDirAll reads the link structure as directory entries
296
-func (s *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
297
- entries := make([]fuse.Dirent, len(s.Nd.Links))
298
- for i, link := range s.Nd.Links {
299
- n := link.Name
300
- if len(n) == 0 {
301
- n = link.Hash.B58String()
247
+func (dir *Directory) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
248
+ var entries []fuse.Dirent
249
+ for _, name := range dir.dir.List() {
250
+ dirent := fuse.Dirent{Name: name}
251
+
252
+ // TODO: make dir.dir.List() return dirinfos
253
+ child, err := dir.dir.Child(name)
254
+ if err != nil {
255
+ return nil, err
256
}
303
- entries[i] = fuse.Dirent{Name: n, Type: fuse.DT_File}
257
+
258
+ switch child.Type() {
259
+ case nsfs.TDir:
260
+ dirent.Type = fuse.DT_Dir
261
+ case nsfs.TFile:
262
+ dirent.Type = fuse.DT_File
263
+ }
264
+
265
+ entries = append(entries, dirent)
266
}
267
268
if len(entries) > 0 {
@@ -309,279 +271,130 @@ func (s *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
271
return nil, fuse.ENOENT
272
}
273
312
-func (s *Node) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
313
- k, err := s.Nd.Key()
274
+func (fi *File) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
275
+ _, err := fi.fi.Seek(req.Offset, os.SEEK_SET)
276
if err != nil {
277
return err
278
}
279
318
- // setup our logging event
319
- lm := make(lgbl.DeferredMap)
320
- lm["fs"] = "ipns"
321
- lm["key"] = func() interface{} { return k.Pretty() }
322
- lm["req_offset"] = req.Offset
323
- lm["req_size"] = req.Size
324
- defer log.EventBegin(ctx, "fuseRead", lm).Done()
325
-
326
- r, err := uio.NewDagReader(ctx, s.Nd, s.Ipfs.DAG)
327
- if err != nil {
328
- return err
329
- }
330
- o, err := r.Seek(req.Offset, os.SEEK_SET)
331
- lm["res_offset"] = o
280
+ fisize, err := fi.fi.Size()
281
if err != nil {
282
return err
283
}
284
336
- buf := resp.Data[:min(req.Size, int(r.Size()))]
337
- n, err := io.ReadFull(r, buf)
285
+ readsize := min(req.Size, int(fisize-req.Offset))
286
+ n, err := io.ReadFull(fi.fi, resp.Data[:readsize])
287
resp.Data = resp.Data[:n]
339
- lm["res_size"] = n
288
return err // may be non-nil / not succeeded
289
}
290
343
-func (n *Node) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
344
- // log.Debugf("ipns: Node Write [%s]: flags = %s, offset = %d, size = %d", n.name, req.Flags.String(), req.Offset, len(req.Data))
345
- if IpnsReadonly {
346
- log.Debug("Attempted to write on readonly ipns filesystem.")
347
- return fuse.EPERM
348
- }
349
-
350
- if n.dagMod == nil {
351
- // Create a DagModifier to allow us to change the existing dag node
352
- dmod, err := mod.NewDagModifier(ctx, n.Nd, n.Ipfs.DAG, n.Ipfs.Pinning.GetManual(), chunk.DefaultSplitter)
353
- if err != nil {
354
- return err
355
- }
356
- n.dagMod = dmod
357
- }
358
- wrote, err := n.dagMod.WriteAt(req.Data, int64(req.Offset))
291
+func (fi *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
292
+ wrote, err := fi.fi.WriteAt(req.Data, req.Offset)
293
if err != nil {
294
return err
295
}
296
resp.Size = wrote
363
- return nil
364
-}
297
366
-func (n *Node) Flush(ctx context.Context, req *fuse.FlushRequest) error {
367
- if IpnsReadonly {
368
- return nil
369
- }
370
-
371
- // If a write has happened
372
- if n.dagMod != nil {
373
- newNode, err := n.dagMod.GetNode()
374
- if err != nil {
375
- return err
376
- }
377
-
378
- if n.parent != nil {
379
- log.Error("updating self in parent!")
380
- err := n.parent.update(n.name, newNode)
381
- if err != nil {
382
- log.Criticalf("error in updating ipns dag tree: %s", err)
383
- // return fuse.ETHISISPRETTYBAD
384
- return err
385
- }
386
- }
387
- n.Nd = newNode
388
-
389
- /*/TEMP
390
- dr, err := mdag.NewDagReader(n.Nd, n.Ipfs.DAG)
391
- if err != nil {
392
- log.Critical("Verification read failed.")
393
- }
394
- b, err := ioutil.ReadAll(dr)
395
- if err != nil {
396
- log.Critical("Verification read failed.")
397
- }
398
- fmt.Println("VERIFICATION READ")
399
- fmt.Printf("READ %d BYTES\n", len(b))
400
- fmt.Println(string(b))
401
- fmt.Println(b)
402
- //*/
403
-
404
- n.dagMod = nil
405
-
406
- n.wasChanged()
407
- }
298
return nil
299
}
300
411
-// Signal that a node in this tree was changed so the root can republish
412
-func (n *Node) wasChanged() {
413
- if IpnsReadonly {
414
- return
415
- }
416
- root := n.nsRoot
417
- if root == nil {
418
- root = n
419
- }
420
-
421
- root.repub.Publish <- struct{}{}
301
+func (fi *File) Flush(ctx context.Context, req *fuse.FlushRequest) error {
302
+ return fi.fi.Close()
303
}
304
424
-func (n *Node) republishRoot() error {
425
-
426
- // We should already be the root, this is just a sanity check
427
- var root *Node
428
- if n.nsRoot != nil {
429
- root = n.nsRoot
430
- } else {
431
- root = n
432
- }
305
+func (fi *File) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
306
+ return fi.fi.Flush()
307
+}
308
434
- // Add any nodes that may be new to the DAG service
435
- err := n.Ipfs.DAG.AddRecursive(root.Nd)
309
+func (fi *File) Forget() {
310
+ err := fi.fi.Flush()
311
if err != nil {
437
- log.Criticalf("ipns: Dag Add Error: %s", err)
438
- return err
312
+ log.Debug("Forget file error: ", err)
313
}
314
+}
315
441
- ndkey, err := root.Nd.Key()
316
+func (dir *Directory) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
317
+ child, err := dir.dir.Mkdir(req.Name)
318
if err != nil {
443
- return err
319
+ return nil, err
320
}
321
446
- err = n.Ipfs.Namesys.Publish(n.Ipfs.Context(), root.key, ndkey)
447
- if err != nil {
448
- return err
449
- }
450
- return nil
322
+ return &Directory{dir: child}, nil
323
}
324
453
-func (n *Node) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
454
- return nil
455
-}
456
-
457
-func (n *Node) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
458
- if IpnsReadonly {
459
- return nil, fuse.EPERM
460
- }
461
- dagnd := &mdag.Node{Data: ft.FolderPBData()}
462
- nnode := n.Nd.Copy()
463
- nnode.AddNodeLink(req.Name, dagnd)
464
-
465
- child := &Node{
466
- Ipfs: n.Ipfs,
467
- Nd: dagnd,
468
- name: req.Name,
469
- }
470
-
471
- if n.nsRoot == nil {
472
- child.nsRoot = n
473
- } else {
474
- child.nsRoot = n.nsRoot
475
- }
476
-
477
- if n.parent != nil {
478
- err := n.parent.update(n.name, nnode)
325
+func (fi *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
326
+ if req.Flags&fuse.OpenTruncate != 0 {
327
+ log.Warning("Need to truncate file!")
328
+ err := fi.fi.Truncate(0)
329
if err != nil {
480
- log.Criticalf("Error updating node: %s", err)
330
return nil, err
331
}
483
- }
484
- n.Nd = nnode
485
-
486
- n.wasChanged()
487
-
488
- return child, nil
489
-}
490
-
491
-func (n *Node) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
492
- //log.Debug("[%s] Received open request! flags = %s", n.name, req.Flags.String())
493
- //TODO: check open flags and truncate if necessary
494
- if req.Flags&fuse.OpenTruncate != 0 {
495
- log.Warning("Need to truncate file!")
496
- n.cached = nil
497
- n.Nd = &mdag.Node{Data: ft.FilePBData(nil, 0)}
332
} else if req.Flags&fuse.OpenAppend != 0 {
333
log.Warning("Need to append to file!")
334
}
501
- return n, nil
335
+ return fi, nil
336
}
337
504
-func (n *Node) Mknod(ctx context.Context, req *fuse.MknodRequest) (fs.Node, error) {
505
- return nil, nil
338
+func (fi *File) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
339
+ return fi.fi.Close()
340
}
341
508
-func (n *Node) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
509
- if IpnsReadonly {
510
- log.Debug("Attempted to call Create on a readonly filesystem.")
511
- return nil, nil, fuse.EPERM
512
- }
513
-
342
+func (dir *Directory) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
343
// New 'empty' file
515
- nd := &mdag.Node{Data: ft.FilePBData(nil, 0)}
516
- child := n.makeChild(req.Name, nd)
517
-
518
- nnode := n.Nd.Copy()
344
+ nd := &dag.Node{Data: ft.FilePBData(nil, 0)}
345
+ err := dir.dir.AddChild(req.Name, nd)
346
+ if err != nil {
347
+ return nil, nil, err
348
+ }
349
520
- err := nnode.AddNodeLink(req.Name, nd)
350
+ child, err := dir.dir.Child(req.Name)
351
if err != nil {
352
return nil, nil, err
353
}
524
- if n.parent != nil {
525
- err := n.parent.update(n.name, nnode)
526
- if err != nil {
527
- log.Criticalf("Error updating node: %s", err)
528
- // Can we panic, please?
529
- return nil, nil, err
530
- }
354
+
355
+ fi, ok := child.(*nsfs.File)
356
+ if !ok {
357
+ return nil, nil, errors.New("child creation failed")
358
}
532
- n.Nd = nnode
533
- n.wasChanged()
359
535
- return child, child, nil
360
+ nodechild := &File{fi: fi}
361
+ return nodechild, nodechild, nil
362
}
363
538
-func (n *Node) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
539
- if IpnsReadonly {
540
- return fuse.EPERM
541
- }
542
-
543
- nnode := n.Nd.Copy()
544
- err := nnode.RemoveNodeLink(req.Name)
364
+func (dir *Directory) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
365
+ err := dir.dir.Unlink(req.Name)
366
if err != nil {
367
return fuse.ENOENT
368
}
548
-
549
- if n.parent != nil {
550
- err := n.parent.update(n.name, nnode)
551
- if err != nil {
552
- log.Criticalf("Error updating node: %s", err)
553
- return err
554
- }
555
- }
556
- n.Nd = nnode
557
- n.wasChanged()
369
return nil
370
}
371
561
-func (n *Node) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
562
- if IpnsReadonly {
563
- log.Debug("Attempted to call Rename on a readonly filesystem.")
564
- return fuse.EPERM
372
+// Rename implements NodeRenamer
373
+func (dir *Directory) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
374
+ cur, err := dir.dir.Child(req.OldName)
375
+ if err != nil {
376
+ return err
377
}
378
567
- var mdn *mdag.Node
568
- for _, l := range n.Nd.Links {
569
- if l.Name == req.OldName {
570
- mdn = l.Node
571
- }
572
- }
573
- if mdn == nil {
574
- log.Critical("nil Link found on rename!")
575
- return fuse.ENOENT
379
+ err = dir.dir.Unlink(req.OldName)
380
+ if err != nil {
381
+ return err
382
}
577
- n.Nd.RemoveNodeLink(req.OldName)
383
384
switch newDir := newDir.(type) {
580
- case *Node:
581
- err := newDir.Nd.AddNodeLink(req.NewName, mdn)
385
+ case *Directory:
386
+ nd, err := cur.GetNode()
387
if err != nil {
388
return err
389
}
390
+
391
+ err = newDir.dir.AddChild(req.NewName, nd)
392
+ if err != nil {
393
+ return err
394
+ }
395
+ case *File:
396
+ log.Critical("Cannot move node into a file!")
397
+ return fuse.EPERM
398
default:
399
log.Critical("Unknown node type for rename target dir!")
400
return errors.New("Unknown fs node type!")
@@ -589,21 +402,11 @@ func (n *Node) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.No
402
return nil
403
}
404
592
-// Updates the child of this node, specified by name to the given newnode
593
-func (n *Node) update(name string, newnode *mdag.Node) error {
594
- nnode, err := n.Nd.UpdateNodeLink(name, newnode)
595
- if err != nil {
596
- return err
597
- }
598
-
599
- if n.parent != nil {
600
- err := n.parent.update(n.name, nnode)
601
- if err != nil {
602
- return err
603
- }
405
+func min(a, b int) int {
406
+ if a < b {
407
+ return a
408
}
605
- n.Nd = nnode
606
- return nil
409
+ return b
410
}
411
412
// to check that out Node implements all the interfaces we want
@@ -615,27 +418,26 @@ type ipnsRoot interface {
418
419
var _ ipnsRoot = (*Root)(nil)
420
618
-type ipnsNode interface {
619
- fs.HandleFlusher
421
+type ipnsDirectory interface {
422
fs.HandleReadDirAller
621
- fs.HandleReader
622
- fs.HandleWriter
423
fs.Node
424
fs.NodeCreater
625
- fs.NodeFsyncer
425
fs.NodeMkdirer
627
- fs.NodeMknoder
628
- fs.NodeOpener
426
fs.NodeRemover
427
fs.NodeRenamer
428
fs.NodeStringLookuper
429
}
430
634
-var _ ipnsNode = (*Node)(nil)
431
+var _ ipnsDirectory = (*Directory)(nil)
432
636
-func min(a, b int) int {
637
- if a < b {
638
- return a
639
- }
640
- return b
433
+type ipnsFile interface {
434
+ fs.HandleFlusher
435
+ fs.HandleReader
436
+ fs.HandleWriter
437
+ fs.HandleReleaser
438
+ fs.Node
439
+ fs.NodeFsyncer
440
+ fs.NodeOpener
441
}
442
+
443
+var _ ipnsFile = (*File)(nil)
fuse/ipns/repub_unix.go
deleted
-44
@@ -1,44 +0,0 @@
1
-// +build !nofuse
2
-
3
-package ipns
4
-
5
-import "time"
6
-
7
-type Republisher struct {
8
- TimeoutLong time.Duration
9
- TimeoutShort time.Duration
10
- Publish chan struct{}
11
- node *Node
12
-}
13
-
14
-func NewRepublisher(n *Node, tshort, tlong time.Duration) *Republisher {
15
- return &Republisher{
16
- TimeoutShort: tshort,
17
- TimeoutLong: tlong,
18
- Publish: make(chan struct{}),
19
- node: n,
20
- }
21
-}
22
-
23
-func (np *Republisher) Run() {
24
- for _ = range np.Publish {
25
- quick := time.After(np.TimeoutShort)
26
- longer := time.After(np.TimeoutLong)
27
-
28
- wait:
29
- select {
30
- case <-quick:
31
- case <-longer:
32
- case <-np.Publish:
33
- quick = time.After(np.TimeoutShort)
34
- goto wait
35
- }
36
-
37
- log.Info("Publishing Changes!")
38
- err := np.node.republishRoot()
39
- if err != nil {
40
- log.Critical("republishRoot error: %s", err)
41
- }
42
-
43
- }
44
-}