@cryptotaxi247 / kubo / commits / b9658f0cb

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 -}