common/upverted_files.go | 9 +++++++++ segments/index.go | 6 ++++++ storage/file-piece.go | 16 ++++++++++++---- storage/file.go | 2 +- torrent.go | 6 +++++- webseed/client.go | 2 +- diff --git a/common/upverted_files.go b/common/upverted_files.go index 1933e16a13bdaff14c620a65f5838a542de3fdfc..8984eb251cfa01ec39b644baa2ccd6b616a8de12 100644 --- a/common/upverted_files.go +++ b/common/upverted_files.go @@ -16,3 +16,12 @@ i++ return l, true } } + +// Returns file segments, BitTorrent v2 aware. +func TorrentOffsetFileSegments(info *metainfo.Info) (ret []segments.Extent) { + files := info.UpvertedFiles() + for _, fi := range files { + ret = append(ret, segments.Extent{fi.TorrentOffset, fi.Length}) + } + return +} diff --git a/segments/index.go b/segments/index.go index 6717dcba463db0ae0ed059a56283b26fc5dd1cdc..888e90a813afcaccef5c4a2c370a486532ba2bae 100644 --- a/segments/index.go +++ b/segments/index.go @@ -17,6 +17,10 @@ type Index struct { segments []Extent } +func NewIndexFromSegments(segments []Extent) Index { + return Index{segments} +} + func (me Index) iterSegments() func() (Length, bool) { return func() (Length, bool) { if len(me.segments) == 0 { @@ -29,6 +33,8 @@ } } } +// Returns true if the callback returns false early, or extents are found in the index for all parts +// of the given extent. func (me Index) Locate(e Extent, output Callback) bool { first := sort.Search(len(me.segments), func(i int) bool { _e := me.segments[i] diff --git a/storage/file-piece.go b/storage/file-piece.go index 47772017e3e1680f28a8e4de7bb74aa40202aa5f..99ff5fc8675129083f4fd1b4d8dbe6fc0bfc7168 100644 --- a/storage/file-piece.go +++ b/storage/file-piece.go @@ -1,6 +1,7 @@ package storage import ( + "github.com/anacrolix/torrent/segments" "io" "log" "os" @@ -32,12 +33,19 @@ verified := true if c.Complete { // If it's allegedly complete, check that its constituent files have the necessary length. - for _, fi := range extentCompleteRequiredLengths(fs.p.Info, fs.p.Offset(), fs.p.Length()) { - s, err := os.Stat(fs.files[fi.fileIndex].path) - if err != nil || s.Size() < fi.length { + if !fs.segmentLocater.Locate(segments.Extent{ + Start: fs.p.Offset(), + Length: fs.p.Length(), + }, func(i int, extent segments.Extent) bool { + file := fs.files[i] + s, err := os.Stat(file.path) + if err != nil || s.Size() < extent.Start+extent.Length { verified = false - break + return false } + return true + }) { + panic("files do not cover piece extent") } } diff --git a/storage/file.go b/storage/file.go index b873964787886d4e62169deecc2141bfc3ef5473..231825fdfeed38f9dfeb1229542c8fde31942bde 100644 --- a/storage/file.go +++ b/storage/file.go @@ -83,7 +83,7 @@ files = append(files, f) } t := &fileTorrentImpl{ files, - segments.NewIndex(common.LengthIterFromUpvertedFiles(upvertedFiles)), + segments.NewIndexFromSegments(common.TorrentOffsetFileSegments(info)), infoHash, fs.opts.PieceCompletion, } diff --git a/torrent.go b/torrent.go index 64e8066012ca015c50f32dc52e46c06035fc572b..a361c4edd64037bfda43b053e44bbc3614698404 100644 --- a/torrent.go +++ b/torrent.go @@ -1133,7 +1133,11 @@ var examineBuf bytes.Buffer if logPieceContents { writers = append(writers, &examineBuf) } - _, err = storagePiece.WriteTo(io.MultiWriter(writers...)) + var written int64 + written, err = storagePiece.WriteTo(io.MultiWriter(writers...)) + if err == nil && written != int64(p.length()) { + err = io.ErrShortWrite + } if logPieceContents { t.logger.WithDefaultLevel(log.Debug).Printf("hashed %q with copy err %v", examineBuf.Bytes(), err) } diff --git a/webseed/client.go b/webseed/client.go index d5ae3ac1db51528b56f2b50c52f2329c5f7b130e..4614a3e407a9bc1adb5c55e2a1cd902eccbd7e64 100644 --- a/webseed/client.go +++ b/webseed/client.go @@ -64,7 +64,7 @@ // In my experience, this is a non-conforming webseed. For example the // http://ia600500.us.archive.org/1/items URLs in archive.org torrents. return } - me.fileIndex = segments.NewIndex(common.LengthIterFromUpvertedFiles(info.UpvertedFiles())) + me.fileIndex = segments.NewIndexFromSegments(common.TorrentOffsetFileSegments(info)) me.info = info me.Pieces.AddRange(0, uint64(info.NumPieces())) }