Fix storage and torrent reader. (#101)
This commit is contained in:
parent
0b89fda8e9
commit
c51c88e511
5 changed files with 133 additions and 62 deletions
3
.github/workflows/build.yaml
vendored
3
.github/workflows/build.yaml
vendored
|
@ -119,8 +119,6 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
make build
|
make build
|
||||||
if: matrix.os != 'windows-latest' # Remove windows builds. Difficult to maintain.
|
|
||||||
|
|
||||||
- name: Cross-compile
|
- name: Cross-compile
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
@ -133,7 +131,6 @@ jobs:
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
name: build-${{ matrix.job_name }}
|
name: build-${{ matrix.job_name }}
|
||||||
path: bin/*
|
path: bin/*
|
||||||
if: matrix.os != 'windows-latest' # Remove windows builds. Difficult to maintain.
|
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
|
|
|
@ -127,17 +127,21 @@ func (s *storage) createParent(p string, f File) error {
|
||||||
func (s *storage) Children(path string) (map[string]File, error) {
|
func (s *storage) Children(path string) (map[string]File, error) {
|
||||||
path = clean(path)
|
path = clean(path)
|
||||||
|
|
||||||
|
files, err := s.getDirFromFs(path)
|
||||||
|
if err == nil {
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
l := make(map[string]File)
|
l := make(map[string]File)
|
||||||
for n, f := range s.children[path] {
|
for n, f := range s.children[path] {
|
||||||
l[n] = f
|
l[n] = f
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := s.children[path]; ok {
|
return l, nil
|
||||||
return l, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.getDirFromFs(path)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *storage) Get(path string) (File, error) {
|
func (s *storage) Get(path string) (File, error) {
|
||||||
|
|
|
@ -73,8 +73,8 @@ func TestStorage(t *testing.T) {
|
||||||
require.Equal(&Dummy{}, file)
|
require.Equal(&Dummy{}, file)
|
||||||
|
|
||||||
files, err = s.Children("/path/special_file.test")
|
files, err = s.Children("/path/special_file.test")
|
||||||
require.Error(err)
|
require.NoError(err)
|
||||||
require.Nil(files)
|
require.NotNil(files)
|
||||||
|
|
||||||
files, err = s.Children("/path/special_file.test/dir/here")
|
files, err = s.Children("/path/special_file.test/dir/here")
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
136
fs/torrent.go
136
fs/torrent.go
|
@ -6,7 +6,9 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/anacrolix/missinggo/v2"
|
||||||
"github.com/anacrolix/torrent"
|
"github.com/anacrolix/torrent"
|
||||||
|
"github.com/distribyted/distribyted/iio"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Filesystem = &Torrent{}
|
var _ Filesystem = &Torrent{}
|
||||||
|
@ -56,9 +58,9 @@ func (fs *Torrent) load() {
|
||||||
<-t.GotInfo()
|
<-t.GotInfo()
|
||||||
for _, file := range t.Files() {
|
for _, file := range t.Files() {
|
||||||
fs.s.Add(&torrentFile{
|
fs.s.Add(&torrentFile{
|
||||||
reader: file.NewReader(),
|
readerFunc: file.NewReader,
|
||||||
len: file.Length(),
|
len: file.Length(),
|
||||||
timeout: fs.readTimeout,
|
timeout: fs.readTimeout,
|
||||||
}, file.Path())
|
}, file.Path())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,15 +78,83 @@ func (fs *Torrent) ReadDir(path string) (map[string]File, error) {
|
||||||
return fs.s.Children(path)
|
return fs.s.Children(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type reader interface {
|
||||||
|
iio.Reader
|
||||||
|
missinggo.ReadContexter
|
||||||
|
}
|
||||||
|
|
||||||
|
type readAtWrapper struct {
|
||||||
|
timeout int
|
||||||
|
mu sync.Mutex
|
||||||
|
|
||||||
|
torrent.Reader
|
||||||
|
io.ReaderAt
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReadAtWrapper(r torrent.Reader, timeout int) reader {
|
||||||
|
return &readAtWrapper{Reader: r, timeout: timeout}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *readAtWrapper) ReadAt(p []byte, off int64) (int, error) {
|
||||||
|
rw.mu.Lock()
|
||||||
|
defer rw.mu.Unlock()
|
||||||
|
_, err := rw.Seek(off, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return readAtLeast(rw, rw.timeout, p, len(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAtLeast(r missinggo.ReadContexter, timeout int, buf []byte, min int) (n int, err error) {
|
||||||
|
if len(buf) < min {
|
||||||
|
return 0, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
for n < min && err == nil {
|
||||||
|
var nn int
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
timer := time.AfterFunc(
|
||||||
|
time.Duration(timeout)*time.Second,
|
||||||
|
func() {
|
||||||
|
cancel()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
nn, err = r.ReadContext(ctx, buf[n:])
|
||||||
|
n += nn
|
||||||
|
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
|
if n >= min {
|
||||||
|
err = nil
|
||||||
|
} else if n > 0 && err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *readAtWrapper) Close() error {
|
||||||
|
rw.mu.Lock()
|
||||||
|
defer rw.mu.Unlock()
|
||||||
|
return rw.Reader.Close()
|
||||||
|
}
|
||||||
|
|
||||||
var _ File = &torrentFile{}
|
var _ File = &torrentFile{}
|
||||||
|
|
||||||
type torrentFile struct {
|
type torrentFile struct {
|
||||||
mu sync.Mutex
|
readerFunc func() torrent.Reader
|
||||||
|
reader reader
|
||||||
|
len int64
|
||||||
|
timeout int
|
||||||
|
}
|
||||||
|
|
||||||
reader torrent.Reader
|
func (d *torrentFile) load() {
|
||||||
len int64
|
if d.reader != nil {
|
||||||
|
return
|
||||||
timeout int
|
}
|
||||||
|
d.reader = newReadAtWrapper(d.readerFunc(), d.timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *torrentFile) Size() int64 {
|
func (d *torrentFile) Size() int64 {
|
||||||
|
@ -96,10 +166,18 @@ func (d *torrentFile) IsDir() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *torrentFile) Close() error {
|
func (d *torrentFile) Close() error {
|
||||||
return d.reader.Close()
|
var err error
|
||||||
|
if d.reader != nil {
|
||||||
|
err = d.reader.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
d.reader = nil
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *torrentFile) Read(p []byte) (n int, err error) {
|
func (d *torrentFile) Read(p []byte) (n int, err error) {
|
||||||
|
d.load()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
timer := time.AfterFunc(
|
timer := time.AfterFunc(
|
||||||
time.Duration(d.timeout)*time.Second,
|
time.Duration(d.timeout)*time.Second,
|
||||||
|
@ -113,41 +191,7 @@ func (d *torrentFile) Read(p []byte) (n int, err error) {
|
||||||
return d.reader.ReadContext(ctx, p)
|
return d.reader.ReadContext(ctx, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *torrentFile) ReadAt(p []byte, off int64) (int, error) {
|
func (d *torrentFile) ReadAt(p []byte, off int64) (n int, err error) {
|
||||||
d.mu.Lock()
|
d.load()
|
||||||
defer d.mu.Unlock()
|
return d.reader.ReadAt(p, off)
|
||||||
_, err := d.reader.Seek(off, io.SeekStart)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
i, err := d.readAtLeast(p, len(p))
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *torrentFile) readAtLeast(buf []byte, min int) (n int, err error) {
|
|
||||||
if len(buf) < min {
|
|
||||||
return 0, io.ErrShortBuffer
|
|
||||||
}
|
|
||||||
for n < min && err == nil {
|
|
||||||
var nn int
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
timer := time.AfterFunc(
|
|
||||||
time.Duration(d.timeout)*time.Second,
|
|
||||||
func() {
|
|
||||||
cancel()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
nn, err = d.reader.ReadContext(ctx, buf[n:])
|
|
||||||
n += nn
|
|
||||||
|
|
||||||
timer.Stop()
|
|
||||||
}
|
|
||||||
if n >= min {
|
|
||||||
err = nil
|
|
||||||
} else if n > 0 && err == io.EOF {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ func TestTorrentFilesystem(t *testing.T) {
|
||||||
require.NoError(f.Close())
|
require.NoError(f.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadAtWrapper(t *testing.T) {
|
func TestReadAtTorrent(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
to, err := Cli.AddMagnet(testMagnet)
|
to, err := Cli.AddMagnet(testMagnet)
|
||||||
|
@ -91,9 +91,9 @@ func TestReadAtWrapper(t *testing.T) {
|
||||||
torrFile := to.Files()[0]
|
torrFile := to.Files()[0]
|
||||||
|
|
||||||
tf := torrentFile{
|
tf := torrentFile{
|
||||||
reader: torrFile.NewReader(),
|
readerFunc: torrFile.NewReader,
|
||||||
len: torrFile.Length(),
|
len: torrFile.Length(),
|
||||||
timeout: 500,
|
timeout: 500,
|
||||||
}
|
}
|
||||||
|
|
||||||
defer tf.Close()
|
defer tf.Close()
|
||||||
|
@ -109,3 +109,29 @@ func TestReadAtWrapper(t *testing.T) {
|
||||||
require.Equal(5, n)
|
require.Equal(5, n)
|
||||||
require.Equal([]byte{0x49, 0x44, 0x33, 0x3, 0x0}, toRead)
|
require.Equal([]byte{0x49, 0x44, 0x33, 0x3, 0x0}, toRead)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReadAtWrapper(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
to, err := Cli.AddMagnet(testMagnet)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
<-to.GotInfo()
|
||||||
|
torrFile := to.Files()[0]
|
||||||
|
|
||||||
|
r := newReadAtWrapper(torrFile.NewReader(), 10)
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
toRead := make([]byte, 5)
|
||||||
|
n, err := r.ReadAt(toRead, 6)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Equal(5, n)
|
||||||
|
require.Equal([]byte{0x0, 0x0, 0x1f, 0x76, 0x54}, toRead)
|
||||||
|
|
||||||
|
n, err = r.ReadAt(toRead, 0)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Equal(5, n)
|
||||||
|
require.Equal([]byte{0x49, 0x44, 0x33, 0x3, 0x0}, toRead)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue