@@ -9,15 +9,23 @@ import (
9
9
"sync"
10
10
"time"
11
11
12
+ bsfetcher "github.com/ipfs/boxo/fetcher/impl/blockservice"
12
13
"github.com/ipfs/boxo/verifcid"
13
14
blocks "github.com/ipfs/go-block-format"
14
15
"github.com/ipfs/go-cid"
15
16
"github.com/ipfs/go-unixfsnode"
17
+ "github.com/ipfs/go-unixfsnode/data"
16
18
"github.com/ipld/go-car"
19
+ dagpb "github.com/ipld/go-codec-dagpb"
17
20
"github.com/ipld/go-ipld-prime"
18
21
"github.com/ipld/go-ipld-prime/datamodel"
19
22
"github.com/ipld/go-ipld-prime/linking"
20
23
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
24
+ "github.com/ipld/go-ipld-prime/node/basicnode"
25
+ "github.com/ipld/go-ipld-prime/schema"
26
+ "github.com/ipld/go-ipld-prime/traversal"
27
+ "github.com/ipld/go-ipld-prime/traversal/selector"
28
+ selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse"
21
29
"github.com/multiformats/go-multihash"
22
30
)
23
31
@@ -135,3 +143,169 @@ func getLinksystem(fn getBlock) *ipld.LinkSystem {
135
143
unixfsnode .AddUnixFSReificationToLinkSystem (& lsys )
136
144
return & lsys
137
145
}
146
+
147
+ // walkGatewaySimpleSelector2 walks the subgraph described by the path and terminal element parameters
148
+ func walkGatewaySimpleSelector2 (ctx context.Context , terminalBlk blocks.Block , dagScope DagScope , entityRange * DagByteRange , lsys * ipld.LinkSystem ) error {
149
+ lctx := ipld.LinkContext {Ctx : ctx }
150
+ var err error
151
+
152
+ // If the scope is the block, we only need the root block of the last element of the path, which we have.
153
+ if dagScope == DagScopeBlock {
154
+ return nil
155
+ }
156
+
157
+ // decode the terminal block into a node
158
+ pc := dagpb .AddSupportToChooser (func (lnk ipld.Link , lnkCtx ipld.LinkContext ) (ipld.NodePrototype , error ) {
159
+ if tlnkNd , ok := lnkCtx .LinkNode .(schema.TypedLinkNode ); ok {
160
+ return tlnkNd .LinkTargetNodePrototype (), nil
161
+ }
162
+ return basicnode .Prototype .Any , nil
163
+ })
164
+
165
+ pathTerminalCidLink := cidlink.Link {Cid : terminalBlk .Cid ()}
166
+ np , err := pc (pathTerminalCidLink , lctx )
167
+ if err != nil {
168
+ return err
169
+ }
170
+
171
+ decoder , err := lsys .DecoderChooser (pathTerminalCidLink )
172
+ if err != nil {
173
+ return err
174
+ }
175
+ nb := np .NewBuilder ()
176
+ blockData := terminalBlk .RawData ()
177
+ if err := decoder (nb , bytes .NewReader (blockData )); err != nil {
178
+ return err
179
+ }
180
+ lastCidNode := nb .Build ()
181
+
182
+ // TODO: Evaluate:
183
+ // Does it matter that we're ignoring the "remainder" portion of the traversal in GetCAR?
184
+ // Does it matter that we're using a linksystem with the UnixFS reifier for dagscope=all?
185
+
186
+ // If we're asking for everything then give it
187
+ if dagScope == DagScopeAll {
188
+ sel , err := selector .ParseSelector (selectorparse .CommonSelector_ExploreAllRecursively )
189
+ if err != nil {
190
+ return err
191
+ }
192
+
193
+ progress := traversal.Progress {
194
+ Cfg : & traversal.Config {
195
+ Ctx : ctx ,
196
+ LinkSystem : * lsys ,
197
+ LinkTargetNodePrototypeChooser : bsfetcher .DefaultPrototypeChooser ,
198
+ LinkVisitOnlyOnce : false , // Despite being safe for the "all" selector we do this walk anyway since this is how we will be receiving the blocks
199
+ },
200
+ }
201
+
202
+ if err := progress .WalkMatching (lastCidNode , sel , func (progress traversal.Progress , node datamodel.Node ) error {
203
+ return nil
204
+ }); err != nil {
205
+ return err
206
+ }
207
+ return nil
208
+ }
209
+
210
+ // From now on, dag-scope=entity!
211
+ // Since we need more of the graph load it to figure out what we have
212
+ // This includes determining if the terminal node is UnixFS or not
213
+ if pbn , ok := lastCidNode .(dagpb.PBNode ); ! ok {
214
+ // If it's not valid dag-pb then we're done
215
+ return nil
216
+ } else if ! pbn .FieldData ().Exists () {
217
+ // If it's not valid UnixFS then we're done
218
+ return nil
219
+ } else if unixfsFieldData , decodeErr := data .DecodeUnixFSData (pbn .Data .Must ().Bytes ()); decodeErr != nil {
220
+ // If it's not valid dag-pb and UnixFS then we're done
221
+ return nil
222
+ } else {
223
+ switch unixfsFieldData .FieldDataType ().Int () {
224
+ case data .Data_Directory , data .Data_Symlink :
225
+ // These types are non-recursive so we're done
226
+ return nil
227
+ case data .Data_Raw , data .Data_Metadata :
228
+ // TODO: for now, we decided to return nil here. The different implementations are inconsistent
229
+ // and UnixFS is not properly specified: https://github.com/ipfs/specs/issues/316.
230
+ // - Is Data_Raw different from Data_File?
231
+ // - Data_Metadata is handled differently in boxo/ipld/unixfs and go-unixfsnode.
232
+ return nil
233
+ case data .Data_HAMTShard :
234
+ // Return all elements in the map
235
+ _ , err := lsys .KnownReifiers ["unixfs-preload" ](lctx , lastCidNode , lsys )
236
+ if err != nil {
237
+ return err
238
+ }
239
+ return nil
240
+ case data .Data_File :
241
+ nd , err := unixfsnode .Reify (lctx , lastCidNode , lsys )
242
+ if err != nil {
243
+ return err
244
+ }
245
+
246
+ fnd , ok := nd .(datamodel.LargeBytesNode )
247
+ if ! ok {
248
+ return fmt .Errorf ("could not process file since it did not present as large bytes" )
249
+ }
250
+ f , err := fnd .AsLargeBytes ()
251
+ if err != nil {
252
+ return err
253
+ }
254
+
255
+ // Get the entity range. If it's empty, assume the defaults (whole file).
256
+ effectiveRange := entityRange
257
+ if effectiveRange == nil {
258
+ effectiveRange = & DagByteRange {
259
+ From : 0 ,
260
+ }
261
+ }
262
+
263
+ from := effectiveRange .From
264
+
265
+ // If we're starting to read based on the end of the file, find out where that is.
266
+ var fileLength int64
267
+ foundFileLength := false
268
+ if effectiveRange .From < 0 {
269
+ fileLength , err = f .Seek (0 , io .SeekEnd )
270
+ if err != nil {
271
+ return err
272
+ }
273
+ from = fileLength + effectiveRange .From
274
+ foundFileLength = true
275
+ }
276
+
277
+ // If we're reading until the end of the file then do it
278
+ if effectiveRange .To == nil {
279
+ if _ , err := f .Seek (from , io .SeekStart ); err != nil {
280
+ return err
281
+ }
282
+ _ , err = io .Copy (io .Discard , f )
283
+ return err
284
+ }
285
+
286
+ to := * effectiveRange .To
287
+ if (* effectiveRange .To ) < 0 && ! foundFileLength {
288
+ fileLength , err = f .Seek (0 , io .SeekEnd )
289
+ if err != nil {
290
+ return err
291
+ }
292
+ to = fileLength + * effectiveRange .To
293
+ foundFileLength = true
294
+ }
295
+
296
+ numToRead := 1 + to - from
297
+ if numToRead < 0 {
298
+ return fmt .Errorf ("tried to read less than zero bytes" )
299
+ }
300
+
301
+ if _ , err := f .Seek (from , io .SeekStart ); err != nil {
302
+ return err
303
+ }
304
+ _ , err = io .CopyN (io .Discard , f , numToRead )
305
+ return err
306
+ default :
307
+ // Not a supported type, so we're done
308
+ return nil
309
+ }
310
+ }
311
+ }
0 commit comments