coreapi unixfs: unixfs.Get
License: MIT Signed-off-by: Łukasz Magiera <magik6k@gmail.com>
Łukasz Magiera committed
Oct 3, 2018 at 17:21 UTC
8dda69575a1c20fd9ed7e458a6cc0f44ebdd9bb3
3 files changed
+216
core/coreapi/interface/unixfs.go
+8
@@ -13,8 +13,16 @@ import (
13
// NOTE: This API is heavily WIP, things are guaranteed to break frequently
14
type UnixfsAPI interface {
15
// Add imports the data from the reader into merkledag file
16
+ //
17
+ // TODO: a long useful comment on how to use this for many different scenarios
18
Add(context.Context, files.File, ...options.UnixfsAddOption) (ResolvedPath, error)
19
20
+ // Get returns a read-only handle to a file tree referenced by a path
21
+ //
22
+ // Note that some implementations of this API may apply the specified context
23
+ // to operations performed on the returned file
24
+ Get(context.Context, Path) (files.File, error)
25
+
26
// Cat returns a reader for the file
27
Cat(context.Context, Path) (Reader, error)
28
core/coreapi/unixfile.go
new
+199
@@ -0,0 +1,199 @@
1
+package coreapi
2
+
3
+import (
4
+ "bytes"
5
+ "context"
6
+ "errors"
7
+ "io"
8
+ "io/ioutil"
9
+ "os"
10
+ gopath "path"
11
+ "time"
12
+
13
+ files "gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit/files"
14
+ ft "gx/ipfs/QmU4x3742bvgfxJsByEDpBnifJqjJdV6x528co4hwKCn46/go-unixfs"
15
+ uio "gx/ipfs/QmU4x3742bvgfxJsByEDpBnifJqjJdV6x528co4hwKCn46/go-unixfs/io"
16
+ dag "gx/ipfs/QmcBoNcAP6qDjgRBew7yjvCqHq7p5jMstE44jPUBWBxzsV/go-merkledag"
17
+ ipld "gx/ipfs/QmdDXJs4axxefSPgK6Y1QhpJWKuDPnGJiqgq4uncb4rFHL/go-ipld-format"
18
+)
19
+
20
+// Number to file to prefetch in directories
21
+// TODO: should we allow setting this via context hint?
22
+const prefetchFiles = 4
23
+
24
+// TODO: this probably belongs in go-unixfs (and could probably replace a chunk of it's interface in the long run)
25
+
26
+type sizeInfo struct {
27
+ size int64
28
+ name string
29
+ modTime time.Time
30
+}
31
+
32
+func (s *sizeInfo) Name() string {
33
+ return s.name
34
+}
35
+
36
+func (s *sizeInfo) Size() int64 {
37
+ return s.size
38
+}
39
+
40
+func (s *sizeInfo) Mode() os.FileMode {
41
+ return 0444 // all read
42
+}
43
+
44
+func (s *sizeInfo) ModTime() time.Time {
45
+ return s.modTime
46
+}
47
+
48
+func (s *sizeInfo) IsDir() bool {
49
+ return false
50
+}
51
+
52
+func (s *sizeInfo) Sys() interface{} {
53
+ return nil
54
+}
55
+
56
+type ufsDirectory struct {
57
+ ctx context.Context
58
+ dserv ipld.DAGService
59
+
60
+ files chan *ipld.Link
61
+
62
+ name string
63
+ path string
64
+}
65
+
66
+func (d *ufsDirectory) Close() error {
67
+ return files.ErrNotReader
68
+}
69
+
70
+func (d *ufsDirectory) Read(_ []byte) (int, error) {
71
+ return 0, files.ErrNotReader
72
+}
73
+
74
+func (d *ufsDirectory) FileName() string {
75
+ return d.name
76
+}
77
+
78
+func (d *ufsDirectory) FullPath() string {
79
+ return d.path
80
+}
81
+
82
+func (d *ufsDirectory) IsDirectory() bool {
83
+ return true
84
+}
85
+
86
+func (d *ufsDirectory) NextFile() (files.File, error) {
87
+ l, ok := <-d.files
88
+ if !ok {
89
+ return nil, io.EOF
90
+ }
91
+
92
+ nd, err := l.GetNode(d.ctx, d.dserv)
93
+ if err != nil {
94
+ return nil, err
95
+ }
96
+
97
+ return newUnixfsFile(d.ctx, d.dserv, nd, l.Name, d)
98
+}
99
+
100
+type ufsFile struct {
101
+ uio.DagReader
102
+
103
+ name string
104
+ path string
105
+}
106
+
107
+func (f *ufsFile) IsDirectory() bool {
108
+ return false
109
+}
110
+
111
+func (f *ufsFile) NextFile() (files.File, error) {
112
+ return nil, files.ErrNotDirectory
113
+}
114
+
115
+func (f *ufsFile) FileName() string {
116
+ return f.name
117
+}
118
+
119
+func (f *ufsFile) FullPath() string {
120
+ return f.path
121
+}
122
+
123
+func (f *ufsFile) Size() (int64, error) {
124
+ return int64(f.DagReader.Size()), nil
125
+}
126
+
127
+func newUnixfsDir(ctx context.Context, dserv ipld.DAGService, nd ipld.Node, name string, path string) (files.File, error) {
128
+ dir, err := uio.NewDirectoryFromNode(dserv, nd)
129
+ if err != nil {
130
+ return nil, err
131
+ }
132
+
133
+ fileCh := make(chan *ipld.Link, prefetchFiles)
134
+ go func() {
135
+ dir.ForEachLink(ctx, func(link *ipld.Link) error {
136
+ select {
137
+ case fileCh <- link:
138
+ case <-ctx.Done():
139
+ return ctx.Err()
140
+ }
141
+ return nil
142
+ })
143
+
144
+ close(fileCh)
145
+ }()
146
+
147
+ return &ufsDirectory{
148
+ ctx: ctx,
149
+ dserv: dserv,
150
+
151
+ files: fileCh,
152
+
153
+ name: name,
154
+ path: path,
155
+ }, nil
156
+}
157
+
158
+func newUnixfsFile(ctx context.Context, dserv ipld.DAGService, nd ipld.Node, name string, parent files.File) (files.File, error) {
159
+ path := name
160
+ if parent != nil {
161
+ path = gopath.Join(parent.FullPath(), name)
162
+ }
163
+
164
+ switch dn := nd.(type) {
165
+ case *dag.ProtoNode:
166
+ fsn, err := ft.FSNodeFromBytes(nd.RawData())
167
+ if err != nil {
168
+ return nil, err
169
+ }
170
+ if fsn.IsDir() {
171
+ return newUnixfsDir(ctx, dserv, nd, name, path)
172
+ }
173
+
174
+ case *dag.RawNode:
175
+
176
+ r := ioutil.NopCloser(bytes.NewReader(dn.RawData()))
177
+ fi := &sizeInfo{
178
+ size: int64(len(dn.RawData())),
179
+ }
180
+
181
+ return files.NewReaderFile("", "", r, fi), nil
182
+ default:
183
+ return nil, errors.New("unknown node type")
184
+ }
185
+
186
+ dr, err := uio.NewDagReader(ctx, nd, dserv)
187
+ if err != nil {
188
+ return nil, err
189
+ }
190
+
191
+ return &ufsFile{
192
+ DagReader: dr,
193
+
194
+ name: name,
195
+ path: path,
196
+ }, nil
197
+}
198
+
199
+var _ os.FileInfo = &sizeInfo{}
core/coreapi/unixfs.go
+9
@@ -110,6 +110,15 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.File, opts ...options
110
return coreiface.IpfsPath(nd.Cid()), nil
111
}
112
113
+func (api *UnixfsAPI) Get(ctx context.Context, p coreiface.Path) (files.File, error) {
114
+ nd, err := api.core().ResolveNode(ctx, p)
115
+ if err != nil {
116
+ return nil, err
117
+ }
118
+
119
+ return newUnixfsFile(ctx, api.node.DAG, nd, "", nil)
120
+}
121
+
122
// Cat returns the data contained by an IPFS or IPNS object(s) at path `p`.
123
func (api *UnixfsAPI) Cat(ctx context.Context, p coreiface.Path) (coreiface.Reader, error) {
124
dget := api.node.DAG // TODO: use a session here once routing perf issues are resolved