]> Sergey Matveev's repositories - btrtrc.git/blob - cmd/torrent-verify/main.go
ae89b5f19a4b311798ec5fcdcff6204c89688040
[btrtrc.git] / cmd / torrent-verify / main.go
1 package main
2
3 import (
4         "bytes"
5         "crypto/sha1"
6         "fmt"
7         "io"
8         "log"
9         "os"
10         "path/filepath"
11
12         "github.com/anacrolix/torrent/storage"
13
14         "github.com/anacrolix/tagflag"
15         "github.com/edsrzf/mmap-go"
16
17         "github.com/anacrolix/torrent/metainfo"
18         "github.com/anacrolix/torrent/mmap_span"
19 )
20
21 func mmapFile(name string) (mm storage.FileMapping, err error) {
22         f, err := os.Open(name)
23         if err != nil {
24                 return
25         }
26         defer func() {
27                 if err != nil {
28                         f.Close()
29                 }
30         }()
31         fi, err := f.Stat()
32         if err != nil {
33                 return
34         }
35         if fi.Size() == 0 {
36                 return
37         }
38         reg, err := mmap.MapRegion(f, -1, mmap.RDONLY, mmap.COPY, 0)
39         if err != nil {
40                 return
41         }
42         return storage.WrapFileMapping(reg, f), nil
43 }
44
45 func verifyTorrent(info *metainfo.Info, root string) error {
46         span := new(mmap_span.MMapSpan)
47         for _, file := range info.UpvertedFiles() {
48                 filename := filepath.Join(append([]string{root, info.Name}, file.Path...)...)
49                 mm, err := mmapFile(filename)
50                 if err != nil {
51                         return err
52                 }
53                 if int64(len(mm.Bytes())) != file.Length {
54                         return fmt.Errorf("file %q has wrong length", filename)
55                 }
56                 span.Append(mm)
57         }
58         span.InitIndex()
59         for i, numPieces := 0, info.NumPieces(); i < numPieces; i += 1 {
60                 p := info.Piece(i)
61                 hash := sha1.New()
62                 _, err := io.Copy(hash, io.NewSectionReader(span, p.Offset(), p.Length()))
63                 if err != nil {
64                         return err
65                 }
66                 good := bytes.Equal(hash.Sum(nil), p.Hash().Bytes())
67                 if !good {
68                         return fmt.Errorf("hash mismatch at piece %d", i)
69                 }
70                 fmt.Printf("%d: %v: %v\n", i, p.Hash(), good)
71         }
72         return nil
73 }
74
75 func main() {
76         log.SetFlags(log.Flags() | log.Lshortfile)
77         flags := struct {
78                 DataDir string
79                 tagflag.StartPos
80                 TorrentFile string
81         }{}
82         tagflag.Parse(&flags)
83         metaInfo, err := metainfo.LoadFromFile(flags.TorrentFile)
84         if err != nil {
85                 log.Fatal(err)
86         }
87         info, err := metaInfo.UnmarshalInfo()
88         if err != nil {
89                 log.Fatalf("error unmarshalling info: %s", err)
90         }
91         err = verifyTorrent(&info, flags.DataDir)
92         if err != nil {
93                 log.Fatalf("torrent failed verification: %s", err)
94         }
95 }