Make opening a torrent in storage an explicit method
This is storage types where opening can fail, like mmap
This commit is contained in:
parent
775cf53809
commit
a5b54f21a1
|
@ -1949,9 +1949,9 @@ func (cl *Client) AddTorrentSpec(spec *TorrentSpec) (T Torrent, new bool, err er
|
|||
if spec.ChunkSize != 0 {
|
||||
t.chunkSize = pp.Integer(spec.ChunkSize)
|
||||
}
|
||||
t.storage = spec.Storage
|
||||
if t.storage == nil {
|
||||
t.storage = cl.defaultStorage
|
||||
t.storageOpener = spec.Storage
|
||||
if t.storageOpener == nil {
|
||||
t.storageOpener = cl.defaultStorage
|
||||
}
|
||||
}
|
||||
if spec.DisplayName != "" {
|
||||
|
|
|
@ -92,7 +92,7 @@ func TestTorrentInitialState(t *testing.T) {
|
|||
return
|
||||
}())
|
||||
tor.chunkSize = 2
|
||||
tor.storage = storage.NewFile(dir)
|
||||
tor.storageOpener = storage.NewFile(dir)
|
||||
// Needed to lock for asynchronous piece verification.
|
||||
tor.cl = new(Client)
|
||||
err := tor.setMetadata(&mi.Info.Info, mi.Info.Bytes)
|
||||
|
@ -464,6 +464,14 @@ func TestMergingTrackersByAddingSpecs(t *testing.T) {
|
|||
|
||||
type badStorage struct{}
|
||||
|
||||
func (me badStorage) OpenTorrent(*metainfo.InfoEx) (storage.Torrent, error) {
|
||||
return me, nil
|
||||
}
|
||||
|
||||
func (me badStorage) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (me badStorage) Piece(p metainfo.Piece) storage.Piece {
|
||||
return badStoragePiece{p}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package mmap_span
|
|||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
|
||||
"github.com/edsrzf/mmap-go"
|
||||
)
|
||||
|
@ -22,10 +23,14 @@ func (me *MMapSpan) Append(mmap mmap.MMap) {
|
|||
me.span = append(me.span, segment{&mmap})
|
||||
}
|
||||
|
||||
func (me MMapSpan) Close() {
|
||||
func (me MMapSpan) Close() error {
|
||||
for _, mMap := range me.span {
|
||||
mMap.(segment).Unmap()
|
||||
err := mMap.(segment).Unmap()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (me MMapSpan) Size() (ret int64) {
|
||||
|
|
|
@ -21,6 +21,14 @@ func NewFile(baseDir string) I {
|
|||
}
|
||||
}
|
||||
|
||||
func (me *fileStorage) OpenTorrent(info *metainfo.InfoEx) (Torrent, error) {
|
||||
return fileTorrentStorage{me}, nil
|
||||
}
|
||||
|
||||
type fileTorrentStorage struct {
|
||||
*fileStorage
|
||||
}
|
||||
|
||||
func (me *fileStorage) Piece(p metainfo.Piece) Piece {
|
||||
_io := &fileStorageTorrent{
|
||||
p.Info,
|
||||
|
@ -34,6 +42,10 @@ func (me *fileStorage) Piece(p metainfo.Piece) Piece {
|
|||
}
|
||||
}
|
||||
|
||||
func (me *fileStorage) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fileStoragePiece struct {
|
||||
*fileStorage
|
||||
p metainfo.Piece
|
||||
|
|
|
@ -19,7 +19,7 @@ func TestShortFile(t *testing.T) {
|
|||
td, err := ioutil.TempDir("", "")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(td)
|
||||
data := NewFile(td)
|
||||
s := NewFile(td)
|
||||
info := &metainfo.InfoEx{
|
||||
Info: metainfo.Info{
|
||||
Name: "a",
|
||||
|
@ -27,12 +27,14 @@ func TestShortFile(t *testing.T) {
|
|||
PieceLength: missinggo.MiB,
|
||||
},
|
||||
}
|
||||
ts, err := s.OpenTorrent(info)
|
||||
assert.NoError(t, err)
|
||||
f, err := os.Create(filepath.Join(td, "a"))
|
||||
err = f.Truncate(1)
|
||||
f.Close()
|
||||
var buf bytes.Buffer
|
||||
p := info.Piece(0)
|
||||
n, err := io.Copy(&buf, io.NewSectionReader(data.Piece(p), 0, p.Length()))
|
||||
n, err := io.Copy(&buf, io.NewSectionReader(ts.Piece(p), 0, p.Length()))
|
||||
assert.EqualValues(t, 1, n)
|
||||
assert.Equal(t, io.ErrUnexpectedEOF, err)
|
||||
}
|
||||
|
|
|
@ -6,11 +6,18 @@ import (
|
|||
"github.com/anacrolix/torrent/metainfo"
|
||||
)
|
||||
|
||||
// Represents data storage for a Torrent.
|
||||
// Represents data storage for an unspecified torrent.
|
||||
type I interface {
|
||||
Piece(metainfo.Piece) Piece
|
||||
OpenTorrent(info *metainfo.InfoEx) (Torrent, error)
|
||||
}
|
||||
|
||||
// Data storage bound to a torrent.
|
||||
type Torrent interface {
|
||||
Piece(metainfo.Piece) Piece
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Interacts with torrent piece data.
|
||||
type Piece interface {
|
||||
// Should return io.EOF only at end of torrent. Short reads due to missing
|
||||
// data should return io.ErrUnexpectedEOF.
|
||||
|
@ -23,20 +30,3 @@ type Piece interface {
|
|||
// Returns true if the piece is complete.
|
||||
GetIsComplete() bool
|
||||
}
|
||||
|
||||
// type PieceStorage interface {
|
||||
// ReadAt(metainfo.Piece, []byte, int64) (int, error)
|
||||
// WriteAt(metainfo.Piece, []byte, int64) (int, error)
|
||||
// MarkComplete(metainfo.Piece) error
|
||||
// GetIsComplete(metainfo.Piece) bool
|
||||
// }
|
||||
|
||||
// type wrappedPieceStorage struct {
|
||||
// ps PieceStorage
|
||||
// }
|
||||
|
||||
// func WrapPieceStorage(ps PieceStorage) I {
|
||||
// return wrappedPieceStorage{ps}
|
||||
// }
|
||||
|
||||
// func (me wrappedPieceStorage) Piece(metainfo.Piece)
|
||||
|
|
|
@ -14,9 +14,7 @@ import (
|
|||
)
|
||||
|
||||
type mmapStorage struct {
|
||||
baseDir string
|
||||
spans map[metainfo.InfoHash]mmap_span.MMapSpan
|
||||
completed map[metainfo.InfoHash]bool
|
||||
baseDir string
|
||||
}
|
||||
|
||||
func NewMMap(baseDir string) I {
|
||||
|
@ -25,36 +23,35 @@ func NewMMap(baseDir string) I {
|
|||
}
|
||||
}
|
||||
|
||||
func (me *mmapStorage) lazySpan(info *metainfo.InfoEx) error {
|
||||
if me.spans == nil {
|
||||
me.spans = make(map[metainfo.InfoHash]mmap_span.MMapSpan)
|
||||
}
|
||||
if _, ok := me.spans[*info.Hash]; ok {
|
||||
return nil
|
||||
}
|
||||
func (me *mmapStorage) OpenTorrent(info *metainfo.InfoEx) (t Torrent, err error) {
|
||||
span, err := MMapTorrent(&info.Info, me.baseDir)
|
||||
if err != nil {
|
||||
return err
|
||||
t = &mmapTorrentStorage{
|
||||
span: span,
|
||||
}
|
||||
me.spans[*info.Hash] = span
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func (me *mmapStorage) Piece(p metainfo.Piece) Piece {
|
||||
err := me.lazySpan(p.Info)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
type mmapTorrentStorage struct {
|
||||
span mmap_span.MMapSpan
|
||||
completed map[metainfo.InfoHash]bool
|
||||
}
|
||||
|
||||
func (me *mmapTorrentStorage) Piece(p metainfo.Piece) Piece {
|
||||
return mmapStoragePiece{
|
||||
storage: me,
|
||||
p: p,
|
||||
ReaderAt: io.NewSectionReader(me.spans[*p.Info.Hash], p.Offset(), p.Length()),
|
||||
WriterAt: missinggo.NewSectionWriter(me.spans[*p.Info.Hash], p.Offset(), p.Length()),
|
||||
ReaderAt: io.NewSectionReader(me.span, p.Offset(), p.Length()),
|
||||
WriterAt: missinggo.NewSectionWriter(me.span, p.Offset(), p.Length()),
|
||||
}
|
||||
}
|
||||
|
||||
func (me *mmapTorrentStorage) Close() error {
|
||||
me.span.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
type mmapStoragePiece struct {
|
||||
storage *mmapStorage
|
||||
storage *mmapTorrentStorage
|
||||
p metainfo.Piece
|
||||
io.ReaderAt
|
||||
io.WriterAt
|
||||
|
|
|
@ -54,7 +54,8 @@ type torrent struct {
|
|||
// get this from the info dict.
|
||||
length int64
|
||||
|
||||
storage storage.I
|
||||
storageOpener storage.I
|
||||
storage storage.Torrent
|
||||
|
||||
// The info dict. Nil if we don't have it (yet).
|
||||
Info *metainfo.InfoEx
|
||||
|
@ -230,6 +231,10 @@ func (t *torrent) setMetadata(md *metainfo.Info, infoBytes []byte) (err error) {
|
|||
Bytes: infoBytes,
|
||||
Hash: &t.InfoHash,
|
||||
}
|
||||
t.storage, err = t.storageOpener.OpenTorrent(t.Info)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t.length = 0
|
||||
for _, f := range t.Info.UpvertedFiles() {
|
||||
t.length += f.Length
|
||||
|
|
Loading…
Reference in New Issue