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