forked from ipld/go-car
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathverify.go
114 lines (103 loc) · 2.44 KB
/
verify.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
package main
import (
"fmt"
"io"
"os"
"github.com/ipfs/go-cid"
carv2 "github.com/ipld/go-car/v2"
"github.com/ipld/go-car/v2/index"
"github.com/multiformats/go-multihash"
"github.com/urfave/cli/v2"
)
// VerifyCar is a command to check a files validity
func VerifyCar(c *cli.Context) error {
if c.Args().Len() == 0 {
return fmt.Errorf("usage: car verify <file.car>")
}
// header
rx, err := carv2.OpenReader(c.Args().First())
if err != nil {
return err
}
defer rx.Close()
roots, err := rx.Roots()
if err != nil {
return err
}
if len(roots) == 0 {
return fmt.Errorf("no roots listed in car header")
}
rootMap := make(map[cid.Cid]struct{})
for _, r := range roots {
rootMap[r] = struct{}{}
}
if rx.Version == 2 {
if rx.Header.DataSize == 0 {
return fmt.Errorf("size of wrapped v1 car listed as '0'")
}
flen, err := os.Stat(c.Args().First())
if err != nil {
return err
}
lengthToIndex := carv2.PragmaSize + carv2.HeaderSize + rx.Header.DataSize
if uint64(flen.Size()) > lengthToIndex && rx.Header.IndexOffset == 0 {
return fmt.Errorf("header claims no index, but extra bytes in file beyond data size")
}
if rx.Header.DataOffset < carv2.PragmaSize+carv2.HeaderSize {
return fmt.Errorf("data offset places data within carv2 header")
}
if rx.Header.IndexOffset < lengthToIndex {
return fmt.Errorf("index offset overlaps with data. data ends at %d. index offset of %d", lengthToIndex, rx.Header.IndexOffset)
}
}
// blocks
fd, err := os.Open(c.Args().First())
if err != nil {
return err
}
rd, err := carv2.NewBlockReader(fd)
if err != nil {
return err
}
cidList := make([]cid.Cid, 0)
for {
blk, err := rd.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
delete(rootMap, blk.Cid())
cidList = append(cidList, blk.Cid())
}
if len(rootMap) > 0 {
return fmt.Errorf("header lists root(s) not present as a block: %v", rootMap)
}
// index
if rx.Version == 2 && rx.Header.HasIndex() {
ir, err := rx.IndexReader()
if err != nil {
return err
}
idx, err := index.ReadFrom(ir)
if err != nil {
return err
}
for _, c := range cidList {
cidHash, err := multihash.Decode(c.Hash())
if err != nil {
return err
}
if cidHash.Code == multihash.IDENTITY {
continue
}
if err := idx.GetAll(c, func(_ uint64) bool {
return true
}); err != nil {
return fmt.Errorf("could not look up known cid %s in index: %w", c, err)
}
}
}
return nil
}