@cryptotaxi247 / kubo / commits / f5f6b664d

fix(cmd): useful errors in dag import (#9945)

* fix: useful errors during dag import Most of the time the error is either a bitflip in one of blocks, or a truncation of car stream. This allows user to understand what happened and at which place in the car stream, making debug more humane. * fix: correct message when root pin failed this also correctly exits CLI commands with code 1 (was silent false-positive 0 before)

Marcin Rataj committed Jun 14, 2023 at 20:55 UTC f5f6b664dbfe7d5c1f0d28754e7383a9a5b230bf
2 files changed +22 -5
core/commands/dag/dag.go
+1 -1
@@ -237,7 +237,7 @@ Specification of CAR formats: https://ipld.io/specs/transport/car/
237 }
238
239 if event.Root.PinErrorMsg != "" {
240 - event.Root.PinErrorMsg = fmt.Sprintf("FAILED: %s", event.Root.PinErrorMsg)
240 + return fmt.Errorf("pinning root %q FAILED: %s", enc.Encode(event.Root.Cid), event.Root.PinErrorMsg)
241 } else {
242 event.Root.PinErrorMsg = "success"
243 }
core/commands/dag/import.go
+21 -4
@@ -2,11 +2,13 @@ package dagcmd
2
3 import (
4 "errors"
5 + "fmt"
6 "io"
7
8 "github.com/ipfs/boxo/coreiface/options"
9 "github.com/ipfs/boxo/files"
10 gocarv2 "github.com/ipfs/boxo/ipld/car/v2"
11 + blocks "github.com/ipfs/go-block-format"
12 cid "github.com/ipfs/go-cid"
13 cmds "github.com/ipfs/go-ipfs-cmds"
14 ipld "github.com/ipfs/go-ipld-format"
@@ -58,6 +60,18 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment
60 roots := cid.NewSet()
61 var blockCount, blockBytesCount uint64
62
63 + // remember last valid block and provide a meaningful error message
64 + // when a truncated/mangled CAR is being imported
65 + importError := func(previous blocks.Block, current blocks.Block, err error) error {
66 + if current != nil {
67 + return fmt.Errorf("import failed at block %q: %w", current.Cid(), err)
68 + }
69 + if previous != nil {
70 + return fmt.Errorf("import failed after block %q: %w", previous.Cid(), err)
71 + }
72 + return fmt.Errorf("import failed: %w", err)
73 + }
74 +
75 it := req.Files.Entries()
76 for it.Next() {
77 file := files.FileFromEntry(it)
@@ -75,6 +89,8 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment
89 // this won't/can't help with not running out of handles
90 defer file.Close()
91
92 + var previous blocks.Block
93 +
94 car, err := gocarv2.NewBlockReader(file)
95 if err != nil {
96 return err
@@ -87,25 +103,26 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment
103 for {
104 block, err := car.Next()
105 if err != nil && err != io.EOF {
90 - return err
106 + return importError(previous, block, err)
107 } else if block == nil {
108 break
109 }
110 if err := cmdutils.CheckBlockSize(req, uint64(len(block.RawData()))); err != nil {
95 - return err
111 + return importError(previous, block, err)
112 }
113
114 // the double-decode is suboptimal, but we need it for batching
115 nd, err := blockDecoder.DecodeNode(req.Context, block)
116 if err != nil {
101 - return err
117 + return importError(previous, block, err)
118 }
119
120 if err := batch.Add(req.Context, nd); err != nil {
105 - return err
121 + return importError(previous, block, err)
122 }
123 blockCount++
124 blockBytesCount += uint64(len(block.RawData()))
125 + previous = block
126 }
127 return nil
128 }()