comments! and cleanup
Jeromy committed
Mar 15, 2015 at 11:13 UTC
f679127d835563a1dd29f787fc58a063314f88c6
4 files changed
+70
-18
core/core.go
+6
-3
@@ -296,13 +296,16 @@ func (n *IpfsNode) teardown() error {
296
n.Repo,
297
}
298
299
- if n.Blocks != nil {
300
- closers = append(closers, n.Blocks)
301
- }
299
+ // Filesystem needs to be closed before network, dht, and blockservice
300
+ // so it can use them as its shutting down
301
if n.IpnsFs != nil {
302
closers = append(closers, n.IpnsFs)
303
}
304
305
+ if n.Blocks != nil {
306
+ closers = append(closers, n.Blocks)
307
+ }
308
+
309
if n.Bootstrapper != nil {
310
closers = append(closers, n.Bootstrapper)
311
}
ipnsfs/dir.go
+2
-4
@@ -70,10 +70,8 @@ func (d *Directory) Open(tpath []string, mode int) (*File, error) {
70
return dir.Open(tpath[1:], mode)
71
}
72
73
-type childCloser interface {
74
- closeChild(string, *dag.Node) error
75
-}
76
-
73
+// closeChild updates the child by the given name to the dag node 'nd'
74
+// and changes its own dag node, then propogates the changes upward
75
func (d *Directory) closeChild(name string, nd *dag.Node) error {
76
_, err := d.fs.dserv.Add(nd)
77
if err != nil {
ipnsfs/file.go
+14
@@ -21,6 +21,7 @@ type File struct {
21
lock sync.Mutex
22
}
23
24
+// NewFile returns a NewFile object with the given parameters
25
func NewFile(name string, node *dag.Node, parent childCloser, fs *Filesystem) (*File, error) {
26
dmod, err := mod.NewDagModifier(context.Background(), node, fs.dserv, fs.pins.GetManual(), chunk.DefaultSplitter)
27
if err != nil {
@@ -35,6 +36,7 @@ func NewFile(name string, node *dag.Node, parent childCloser, fs *Filesystem) (*
36
}, nil
37
}
38
39
+// Write writes the given data to the file at its current offset
40
func (fi *File) Write(b []byte) (int, error) {
41
fi.Lock()
42
defer fi.Unlock()
@@ -42,12 +44,15 @@ func (fi *File) Write(b []byte) (int, error) {
44
return fi.mod.Write(b)
45
}
46
47
+// Read reads into the given buffer from the current offset
48
func (fi *File) Read(b []byte) (int, error) {
49
fi.Lock()
50
defer fi.Unlock()
51
return fi.mod.Read(b)
52
}
53
54
+// Close flushes, then propogates the modified dag node up the directory structure
55
+// and signals a republish to occur
56
func (fi *File) Close() error {
57
fi.Lock()
58
defer fi.Unlock()
@@ -75,18 +80,21 @@ func (fi *File) Close() error {
80
return nil
81
}
82
83
+// Flush flushes the changes in the file to disk
84
func (fi *File) Flush() error {
85
fi.Lock()
86
defer fi.Unlock()
87
return fi.mod.Flush()
88
}
89
90
+// Seek implements io.Seeker
91
func (fi *File) Seek(offset int64, whence int) (int64, error) {
92
fi.Lock()
93
defer fi.Unlock()
94
return fi.mod.Seek(offset, whence)
95
}
96
97
+// Write At writes the given bytes at the offset 'at'
98
func (fi *File) WriteAt(b []byte, at int64) (int, error) {
99
fi.Lock()
100
defer fi.Unlock()
@@ -94,18 +102,21 @@ func (fi *File) WriteAt(b []byte, at int64) (int, error) {
102
return fi.mod.WriteAt(b, at)
103
}
104
105
+// Size returns the size of this file
106
func (fi *File) Size() (int64, error) {
107
fi.Lock()
108
defer fi.Unlock()
109
return fi.mod.Size()
110
}
111
112
+// GetNode returns the dag node associated with this file
113
func (fi *File) GetNode() (*dag.Node, error) {
114
fi.Lock()
115
defer fi.Unlock()
116
return fi.mod.GetNode()
117
}
118
119
+// Truncate truncates the file to size
120
func (fi *File) Truncate(size int64) error {
121
fi.Lock()
122
defer fi.Unlock()
@@ -113,14 +124,17 @@ func (fi *File) Truncate(size int64) error {
124
return fi.mod.Truncate(size)
125
}
126
127
+// Type returns the type FSNode this is
128
func (fi *File) Type() NodeType {
129
return TFile
130
}
131
132
+// Lock the file
133
func (fi *File) Lock() {
134
fi.lock.Lock()
135
}
136
137
+// Unlock the file
138
func (fi *File) Unlock() {
139
fi.lock.Unlock()
140
}
ipnsfs/system.go
+48
-11
@@ -1,3 +1,13 @@
1
+// package ipnsfs implements an in memory model of a mutable ipns filesystem,
2
+// to be used by the fuse filesystem.
3
+//
4
+// It consists of four main structs:
5
+// 1) The Filesystem
6
+// The filesystem serves as a container and entry point for the ipns filesystem
7
+// 2) KeyRoots
8
+// KeyRoots represent the root of the keyspace controlled by a given keypair
9
+// 3) Directories
10
+// 4) Files
11
package ipnsfs
12
13
import (
@@ -5,6 +15,7 @@ import (
15
"fmt"
16
"os"
17
"strings"
18
+ "sync"
19
"time"
20
21
dag "github.com/jbenet/go-ipfs/merkledag"
@@ -33,6 +44,7 @@ type Filesystem struct {
44
roots map[string]*KeyRoot
45
}
46
47
+// NewFilesystem instantiates an ipns filesystem using the given parameters and locally owned keys
48
func NewFilesystem(ctx context.Context, ds dag.DAGService, nsys namesys.NameSystem, pins pin.Pinner, keys ...ci.PrivKey) (*Filesystem, error) {
49
roots := make(map[string]*KeyRoot)
50
fs := &Filesystem{
@@ -47,7 +59,7 @@ func NewFilesystem(ctx context.Context, ds dag.DAGService, nsys namesys.NameSyst
59
return nil, err
60
}
61
50
- root, err := fs.NewKeyRoot(ctx, k)
62
+ root, err := fs.newKeyRoot(ctx, k)
63
if err != nil {
64
return nil, err
65
}
@@ -57,6 +69,7 @@ func NewFilesystem(ctx context.Context, ds dag.DAGService, nsys namesys.NameSyst
69
return fs, nil
70
}
71
72
+// Open opens a file at the given path
73
func (fs *Filesystem) Open(tpath string, mode int) (*File, error) {
74
pathelem := strings.Split(tpath, "/")
75
r, ok := fs.roots[pathelem[0]]
@@ -68,15 +81,23 @@ func (fs *Filesystem) Open(tpath string, mode int) (*File, error) {
81
}
82
83
func (fs *Filesystem) Close() error {
84
+ wg := sync.WaitGroup{}
85
for _, r := range fs.roots {
72
- err := r.Publish(context.TODO())
73
- if err != nil {
74
- return err
75
- }
86
+ wg.Add(1)
87
+ go func(r *KeyRoot) {
88
+ defer wg.Done()
89
+ err := r.Publish(context.TODO())
90
+ if err != nil {
91
+ log.Error(err)
92
+ return
93
+ }
94
+ }(r)
95
}
96
+ wg.Wait()
97
return nil
98
}
99
100
+// GetRoot returns the KeyRoot of the given name
101
func (fs *Filesystem) GetRoot(name string) (*KeyRoot, error) {
102
r, ok := fs.roots[name]
103
if ok {
@@ -85,6 +106,10 @@ func (fs *Filesystem) GetRoot(name string) (*KeyRoot, error) {
106
return nil, os.ErrNotExist
107
}
108
109
+type childCloser interface {
110
+ closeChild(string, *dag.Node) error
111
+}
112
+
113
type NodeType int
114
115
const (
@@ -92,6 +117,7 @@ const (
117
TDir
118
)
119
120
+// FSNode represents any node (directory, root, or file) in the ipns filesystem
121
type FSNode interface {
122
GetNode() (*dag.Node, error)
123
Type() NodeType
@@ -115,7 +141,9 @@ type KeyRoot struct {
141
repub *Republisher
142
}
143
118
-func (fs *Filesystem) NewKeyRoot(parent context.Context, k ci.PrivKey) (*KeyRoot, error) {
144
+// newKeyRoot creates a new KeyRoot for the given key, and starts up a republisher routine
145
+// for it
146
+func (fs *Filesystem) newKeyRoot(parent context.Context, k ci.PrivKey) (*KeyRoot, error) {
147
hash, err := k.GetPublic().Hash()
148
if err != nil {
149
return nil, err
@@ -180,14 +208,14 @@ func (kr *KeyRoot) GetValue() FSNode {
208
209
func (kr *KeyRoot) Open(tpath []string, mode int) (*File, error) {
210
if kr.val == nil {
183
- // No entry here... what should we do?
184
- panic("nyi")
211
+ // No entry here. KeyRoot was created incorrectly
212
+ panic("nil keyroot.val, improperly constructed keyroot")
213
}
214
if len(tpath) > 0 {
215
// Make sure our root is a directory
216
dir, ok := kr.val.(*Directory)
217
if !ok {
190
- return nil, fmt.Errorf("no such file or directory: %s", tpath[0])
218
+ return nil, os.ErrNotExist
219
}
220
221
return dir.Open(tpath, mode)
@@ -222,6 +250,7 @@ func (kr *KeyRoot) Publish(ctx context.Context) error {
250
return err
251
}
252
253
+ // Holding this lock so our child doesnt change out from under us
254
child.Lock()
255
k, err := kr.fs.dserv.Add(nd)
256
if err != nil {
@@ -230,6 +259,8 @@ func (kr *KeyRoot) Publish(ctx context.Context) error {
259
}
260
child.Unlock()
261
// Dont want to hold the lock while we publish
262
+ // otherwise we are holding the lock through a costly
263
+ // network operation
264
265
fmt.Println("Publishing!")
266
return kr.fs.nsys.Publish(ctx, kr.key, k)
@@ -243,6 +274,8 @@ type Republisher struct {
274
root *KeyRoot
275
}
276
277
+// NewRepublisher creates a new Republisher object to republish the given keyroot
278
+// using the given short and long time intervals
279
func NewRepublisher(root *KeyRoot, tshort, tlong time.Duration) *Republisher {
280
return &Republisher{
281
TimeoutShort: tshort,
@@ -252,6 +285,9 @@ func NewRepublisher(root *KeyRoot, tshort, tlong time.Duration) *Republisher {
285
}
286
}
287
288
+// Touch signals that an update has occurred since the last publish.
289
+// Multiple consecutive touches may extend the time period before
290
+// the next Publish occurs in order to more efficiently batch updates
291
func (np *Republisher) Touch() {
292
select {
293
case np.Publish <- struct{}{}:
@@ -259,6 +295,7 @@ func (np *Republisher) Touch() {
295
}
296
}
297
298
+// Run is the main republisher loop
299
func (np *Republisher) Run(ctx context.Context) {
300
for {
301
select {
@@ -268,13 +305,13 @@ func (np *Republisher) Run(ctx context.Context) {
305
306
wait:
307
select {
271
- case <-quick:
272
- case <-longer:
308
case <-ctx.Done():
309
return
310
case <-np.Publish:
311
quick = time.After(np.TimeoutShort)
312
goto wait
313
+ case <-quick:
314
+ case <-longer:
315
}
316
317
log.Info("Publishing Changes!")