fix locking and race conditions throughout ipnsfs and the pinner
Jeromy committed
Mar 13, 2015 at 21:17 UTC
dc80116d7641544e67ef09838a100e92e1507de8
3 files changed
+63
-61
ipnsfs/dir.go
+16
-20
@@ -15,17 +15,16 @@ var ErrNotYetImplemented = errors.New("not yet implemented")
15
var ErrInvalidChild = errors.New("invalid child node")
16
17
type Directory struct {
18
- fs *Filesystem
19
- parent childCloser
18
+ fs *Filesystem
19
+ parent childCloser
20
+
21
childDirs map[string]*Directory
21
- files map[string]*file
22
+ files map[string]*File
23
23
- node *dag.Node
24
- name string
24
lock sync.Mutex
25
+ node *dag.Node
26
27
- ref int
28
- refLock sync.Mutex
27
+ name string
28
}
29
30
func NewDirectory(name string, node *dag.Node, parent childCloser, fs *Filesystem) *Directory {
@@ -35,18 +34,19 @@ func NewDirectory(name string, node *dag.Node, parent childCloser, fs *Filesyste
34
node: node,
35
parent: parent,
36
childDirs: make(map[string]*Directory),
38
- files: make(map[string]*file),
37
+ files: make(map[string]*File),
38
}
39
}
40
42
-func (d *Directory) Open(tpath []string, mode int) (File, error) {
41
+// Open opens a file at the given path 'tpath'
42
+func (d *Directory) Open(tpath []string, mode int) (*File, error) {
43
if len(tpath) == 0 {
44
return nil, ErrIsDirectory
45
}
46
if len(tpath) == 1 {
47
fi, err := d.childFile(tpath[0])
48
if err == nil {
49
- return fi.withMode(mode), nil
49
+ return fi, nil
50
}
51
52
if mode|os.O_CREATE != 0 {
@@ -57,7 +57,7 @@ func (d *Directory) Open(tpath []string, mode int) (File, error) {
57
return nil, err
58
}
59
d.files[tpath[0]] = nfi
60
- return nfi.withMode(mode), nil
60
+ return nfi, nil
61
}
62
63
return nil, ErrNoSuch
@@ -102,7 +102,7 @@ func (d *Directory) Type() NodeType {
102
return TDir
103
}
104
105
-func (d *Directory) childFile(name string) (*file, error) {
105
+func (d *Directory) childFile(name string) (*File, error) {
106
fi, ok := d.files[name]
107
if ok {
108
return fi, nil
@@ -334,14 +334,10 @@ func (d *Directory) GetNode() (*dag.Node, error) {
334
return d.node, nil
335
}
336
337
-func (d *Directory) Upref() {
338
- d.refLock.Lock()
339
- d.ref++
340
- d.refLock.Unlock()
337
+func (d *Directory) Lock() {
338
+ d.lock.Lock()
339
}
340
343
-func (d *Directory) Deref() {
344
- d.refLock.Lock()
345
- d.ref--
346
- d.refLock.Unlock()
341
+func (d *Directory) Unlock() {
342
+ d.lock.Unlock()
343
}
ipnsfs/file.go
+38
-38
@@ -1,9 +1,7 @@
1
package ipnsfs
2
3
import (
4
- "errors"
5
- "io"
6
- "os"
4
+ "sync"
5
6
chunk "github.com/jbenet/go-ipfs/importer/chunk"
7
dag "github.com/jbenet/go-ipfs/merkledag"
@@ -12,33 +10,24 @@ import (
10
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
11
)
12
15
-type File interface {
16
- io.ReadWriteCloser
17
- io.WriterAt
18
- Seek(int64, int) (int64, error)
19
- Size() (int64, error)
20
- Flush() error
21
- Truncate(int64) error
22
- FSNode
23
-}
24
-
25
-type file struct {
13
+type File struct {
14
parent childCloser
15
fs *Filesystem
16
17
name string
18
hasChanges bool
19
32
- mod *mod.DagModifier
20
+ mod *mod.DagModifier
21
+ lock sync.Mutex
22
}
23
35
-func NewFile(name string, node *dag.Node, parent childCloser, fs *Filesystem) (*file, error) {
24
+func NewFile(name string, node *dag.Node, parent childCloser, fs *Filesystem) (*File, error) {
25
dmod, err := mod.NewDagModifier(context.Background(), node, fs.dserv, fs.pins.GetManual(), chunk.DefaultSplitter)
26
if err != nil {
27
return nil, err
28
}
29
41
- return &file{
30
+ return &File{
31
fs: fs,
32
parent: parent,
33
name: name,
@@ -46,16 +35,20 @@ func NewFile(name string, node *dag.Node, parent childCloser, fs *Filesystem) (*
35
}, nil
36
}
37
49
-func (fi *file) Write(b []byte) (int, error) {
38
+func (fi *File) Write(b []byte) (int, error) {
39
fi.hasChanges = true
40
return fi.mod.Write(b)
41
}
42
54
-func (fi *file) Read(b []byte) (int, error) {
43
+func (fi *File) Read(b []byte) (int, error) {
44
+ fi.lock.Lock()
45
+ defer fi.lock.Unlock()
46
return fi.mod.Read(b)
47
}
48
58
-func (fi *file) Close() error {
49
+func (fi *File) Close() error {
50
+ fi.lock.Lock()
51
+ defer fi.lock.Unlock()
52
if fi.hasChanges {
53
err := fi.mod.Flush()
54
if err != nil {
@@ -67,7 +60,9 @@ func (fi *file) Close() error {
60
return err
61
}
62
63
+ fi.lock.Unlock()
64
err = fi.parent.closeChild(fi.name, nd)
65
+ fi.lock.Lock()
66
if err != nil {
67
return err
68
}
@@ -78,47 +73,52 @@ func (fi *file) Close() error {
73
return nil
74
}
75
81
-func (fi *file) Flush() error {
76
+func (fi *File) Flush() error {
77
+ fi.lock.Lock()
78
+ defer fi.lock.Unlock()
79
return fi.mod.Flush()
80
}
81
85
-func (fi *file) withMode(mode int) File {
86
- if mode == os.O_RDONLY {
87
- return &readOnlyFile{fi}
88
- }
89
- return fi
90
-}
91
-
92
-func (fi *file) Seek(offset int64, whence int) (int64, error) {
82
+func (fi *File) Seek(offset int64, whence int) (int64, error) {
83
+ fi.lock.Lock()
84
+ defer fi.lock.Unlock()
85
return fi.mod.Seek(offset, whence)
86
}
87
96
-func (fi *file) WriteAt(b []byte, at int64) (int, error) {
88
+func (fi *File) WriteAt(b []byte, at int64) (int, error) {
89
+ fi.lock.Lock()
90
+ defer fi.lock.Unlock()
91
fi.hasChanges = true
92
return fi.mod.WriteAt(b, at)
93
}
94
101
-func (fi *file) Size() (int64, error) {
95
+func (fi *File) Size() (int64, error) {
96
+ fi.lock.Lock()
97
+ defer fi.lock.Unlock()
98
return fi.mod.Size()
99
}
100
105
-func (fi *file) GetNode() (*dag.Node, error) {
101
+func (fi *File) GetNode() (*dag.Node, error) {
102
+ fi.lock.Lock()
103
+ defer fi.lock.Unlock()
104
return fi.mod.GetNode()
105
}
106
109
-func (fi *file) Truncate(size int64) error {
107
+func (fi *File) Truncate(size int64) error {
108
+ fi.lock.Lock()
109
+ defer fi.lock.Unlock()
110
fi.hasChanges = true
111
return fi.mod.Truncate(size)
112
}
113
114
-func (fi *file) Type() NodeType {
114
+func (fi *File) Type() NodeType {
115
return TFile
116
}
117
118
-type readOnlyFile struct {
119
- *file
118
+func (fi *File) Lock() {
119
+ fi.lock.Lock()
120
}
121
122
-func (ro *readOnlyFile) Write([]byte) (int, error) {
123
- return 0, errors.New("permission denied: file readonly")
122
+func (fi *File) Unlock() {
123
+ fi.lock.Unlock()
124
}
ipnsfs/system.go
+9
-3
@@ -58,7 +58,7 @@ func NewFilesystem(ctx context.Context, ds dag.DAGService, nsys namesys.NameSyst
58
return fs, nil
59
}
60
61
-func (fs *Filesystem) Open(tpath string, mode int) (File, error) {
61
+func (fs *Filesystem) Open(tpath string, mode int) (*File, error) {
62
pathelem := strings.Split(tpath, "/")
63
r, ok := fs.roots[pathelem[0]]
64
if !ok {
@@ -96,6 +96,8 @@ const (
96
type FSNode interface {
97
GetNode() (*dag.Node, error)
98
Type() NodeType
99
+ Lock()
100
+ Unlock()
101
}
102
103
// KeyRoot represents the root of a filesystem tree pointed to by a given keypair
@@ -177,7 +179,7 @@ func (kr *KeyRoot) GetValue() FSNode {
179
return kr.val
180
}
181
180
-func (kr *KeyRoot) Open(tpath []string, mode int) (File, error) {
182
+func (kr *KeyRoot) Open(tpath []string, mode int) (*File, error) {
183
if kr.val == nil {
184
// No entry here... what should we do?
185
panic("nyi")
@@ -195,7 +197,7 @@ func (kr *KeyRoot) Open(tpath []string, mode int) (File, error) {
197
switch t := kr.val.(type) {
198
case *Directory:
199
return nil, ErrIsDirectory
198
- case File:
200
+ case *File:
201
return t, nil
202
default:
203
panic("unrecognized type, should not happen")
@@ -221,10 +223,14 @@ func (kr *KeyRoot) Publish(ctx context.Context) error {
223
return err
224
}
225
226
+ child.Lock()
227
k, err := kr.fs.dserv.Add(nd)
228
if err != nil {
229
+ child.Unlock()
230
return err
231
}
232
+ child.Unlock()
233
+ // Dont want to hold the lock while we publish
234
235
fmt.Println("Publishing!")
236
return kr.fs.nsys.Publish(ctx, kr.key, k)