@cryptotaxi247 / kubo / commits / dc80116d7

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)