-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathpath.go
267 lines (253 loc) · 6.62 KB
/
path.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
package buckets
import (
"context"
"fmt"
gopath "path"
"strings"
c "github.com/ipfs/go-cid"
ipld "github.com/ipfs/go-ipld-format"
mdag "github.com/ipfs/go-merkledag"
"github.com/ipfs/go-unixfs"
ifaceopts "github.com/ipfs/interface-go-ipfs-core/options"
"github.com/ipfs/interface-go-ipfs-core/path"
"github.com/textileio/go-buckets/collection"
"github.com/textileio/go-buckets/dag"
"github.com/textileio/go-threads/core/did"
core "github.com/textileio/go-threads/core/thread"
)
// parsePath validates and cleans a bucket path.
func parsePath(pth string) (fpth string, err error) {
if strings.Contains(pth, collection.SeedName) {
err = fmt.Errorf("paths containing %s are not allowed", collection.SeedName)
return
}
return trimSlash(pth), nil
}
// trimSlash removes a slash prefix from the path.
func trimSlash(pth string) string {
return strings.TrimPrefix(pth, "/")
}
// listPaths returns a PathItem describing the bucket path.
func (b *Buckets) listPath(ctx context.Context, bucket *collection.Bucket, pth path.Path) (*PathItem, error) {
item, err := b.pathToItem(ctx, bucket, pth, true)
if err != nil {
return nil, err
}
if pth.String() == bucket.Path {
item.Name = bucket.Name
}
return item, nil
}
// pathToItem returns items at path, optionally including one level down of links.
func (b *Buckets) pathToItem(
ctx context.Context,
bucket *collection.Bucket,
pth path.Path,
includeNextLevel bool,
) (*PathItem, error) {
var linkKey []byte
if bucket != nil {
linkKey = bucket.GetLinkEncryptionKey()
}
n, err := dag.GetNodeAtPath(ctx, b.ipfs, pth, linkKey)
if err != nil {
return nil, err
}
return b.nodeToItem(ctx, bucket, n, pth.String(), linkKey, false, includeNextLevel)
}
// nodeToItem returns a path item from an IPLD node.
func (b *Buckets) nodeToItem(
ctx context.Context,
bucket *collection.Bucket,
node ipld.Node,
pth string,
key []byte,
decrypt,
includeNextLevel bool,
) (*PathItem, error) {
if decrypt && key != nil {
var err error
node, _, err = dag.DecryptNode(node, key)
if err != nil {
return nil, err
}
}
stat, err := node.Stat()
if err != nil {
return nil, err
}
item := &PathItem{
Cid: node.Cid().String(),
Name: gopath.Base(pth),
Path: pth,
Size: int64(stat.CumulativeSize),
}
if bucket != nil {
name := trimSlash(strings.TrimPrefix(pth, bucket.Path))
md, _, ok := bucket.GetMetadataForPath(name, false)
if !ok {
return nil, fmt.Errorf("could not resolve path: %s", pth)
}
item.Metadata = md
}
if pn, ok := node.(*mdag.ProtoNode); ok {
fn, _ := unixfs.FSNodeFromBytes(pn.Data())
if fn != nil && fn.IsDir() {
item.IsDir = true
}
}
if item.IsDir {
for _, l := range node.Links() {
if l.Name == "" {
break
}
if includeNextLevel {
p := gopath.Join(pth, l.Name)
n, err := l.GetNode(ctx, b.ipfs.Dag())
if err != nil {
return nil, err
}
i, err := b.nodeToItem(ctx, bucket, n, p, key, true, false)
if err != nil {
return nil, err
}
item.Items = append(item.Items, *i)
}
item.ItemsCount++
}
}
return item, nil
}
// removePath removes a path from a bucket.
func (b *Buckets) removePath(
ctx context.Context,
instance *collection.Bucket,
pth string,
) (context.Context, path.Resolved, error) {
bpth := path.New(instance.Path)
var dirPath path.Resolved
var err error
if instance.IsPrivate() {
ctx, dirPath, err = dag.RemoveNodeAtPath(
ctx,
b.ipfs,
path.Join(bpth, pth),
instance.GetLinkEncryptionKey(),
)
if err != nil {
return ctx, nil, fmt.Errorf("remove node failed: %v", err)
}
} else {
dirPath, err = b.ipfs.Object().RmLink(ctx, bpth, pth)
if err != nil {
return ctx, nil, err
}
ctx, err = dag.UpdateOrAddPin(ctx, b.ipfs, bpth, dirPath)
if err != nil {
return ctx, nil, fmt.Errorf("update pin failed: %v", err)
}
}
return ctx, dirPath, nil
}
// getBucketAndPath returns a bucket instance and full bucket path.
func (b *Buckets) getBucketAndPath(
ctx context.Context,
thread core.ID,
key string,
identity did.Token,
pth string,
) (*collection.Bucket, path.Path, error) {
instance, err := b.c.GetSafe(ctx, thread, key, collection.WithIdentity(identity))
if err != nil {
return nil, nil, err
}
bpth, err := getBucketPath(instance, pth)
return instance, bpth, err
}
// getBucketPath concatenates the bucket root path with path.
func getBucketPath(bucket *collection.Bucket, pth string) (path.Path, error) {
bpth := path.New(gopath.Join(bucket.Path, pth))
if err := bpth.IsValid(); err != nil {
return nil, err
}
return bpth, nil
}
// setPathFromExistingCid sets the path with a cid from the network, encrypting with file key if present.
func (b *Buckets) setPathFromExistingCid(
ctx context.Context,
instance *collection.Bucket,
buckPath path.Path,
destPath string,
cid c.Cid,
linkKey,
fileKey []byte,
) (context.Context, path.Resolved, error) {
var dirPath path.Resolved
if destPath == "" {
sn, err := dag.MakeBucketSeed(fileKey)
if err != nil {
return ctx, nil, err
}
ctx, dirPath, err = dag.CreateBucketPathWithCid(ctx, b.ipfs, destPath, cid, linkKey, fileKey, sn)
if err != nil {
return ctx, nil, fmt.Errorf("generating bucket new root: %v", err)
}
if instance.IsPrivate() {
buckPathResolved, err := b.ipfs.ResolvePath(ctx, buckPath)
if err != nil {
return ctx, nil, fmt.Errorf("resolving path: %v", err)
}
ctx, err = dag.UnpinNodeAndBranch(ctx, b.ipfs, buckPathResolved, linkKey)
if err != nil {
return ctx, nil, fmt.Errorf("unpinning pinned root: %v", err)
}
} else {
ctx, err = dag.UnpinPath(ctx, b.ipfs, buckPath)
if err != nil {
return ctx, nil, fmt.Errorf("updating pinned root: %v", err)
}
}
} else {
bootPath := path.IpfsPath(cid)
if instance.IsPrivate() {
n, nodes, err := dag.NewDirFromExistingPath(
ctx,
b.ipfs,
bootPath,
destPath,
linkKey,
fileKey,
nil,
"",
)
if err != nil {
return ctx, nil, fmt.Errorf("resolving remote path: %v", err)
}
ctx, dirPath, err = dag.InsertNodeAtPath(ctx, b.ipfs, n, path.Join(buckPath, destPath), linkKey)
if err != nil {
return ctx, nil, fmt.Errorf("updating pinned root: %v", err)
}
ctx, err = dag.AddAndPinNodes(ctx, b.ipfs, nodes)
if err != nil {
return ctx, nil, err
}
} else {
var err error
dirPath, err = b.ipfs.Object().AddLink(
ctx,
buckPath,
destPath,
bootPath,
ifaceopts.Object.Create(true),
)
if err != nil {
return ctx, nil, fmt.Errorf("adding folder: %v", err)
}
ctx, err = dag.UpdateOrAddPin(ctx, b.ipfs, buckPath, dirPath)
if err != nil {
return ctx, nil, fmt.Errorf("updating pinned root: %v", err)
}
}
}
return ctx, dirPath, nil
}