import (
"context"
- "fmt"
+ "io"
- "bazil.org/fuse"
- "bazil.org/fuse/fs"
+ "github.com/anacrolix/fuse"
+ "github.com/anacrolix/fuse/fs"
+ "github.com/anacrolix/missinggo/v2"
+
+ "github.com/anacrolix/torrent"
)
type fileHandle struct {
fn fileNode
+ tf *torrent.File
}
-var _ fs.HandleReader = fileHandle{}
+var _ interface {
+ fs.HandleReader
+ fs.HandleReleaser
+} = fileHandle{}
func (me fileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
torrentfsReadRequests.Add(1)
if req.Dir {
panic("read on directory")
}
- size := req.Size
- fileLeft := int64(me.fn.size) - req.Offset
- if fileLeft < 0 {
- fileLeft = 0
- }
- if fileLeft < int64(size) {
- size = int(fileLeft)
- }
- resp.Data = resp.Data[:size]
- if len(resp.Data) == 0 {
- return nil
- }
- torrentOff := me.fn.TorrentOffset + req.Offset
- n, err := readFull(ctx, me.fn.FS, me.fn.t, torrentOff, resp.Data)
+ r := me.tf.NewReader()
+ defer r.Close()
+ pos, err := r.Seek(req.Offset, io.SeekStart)
if err != nil {
- return err
+ panic(err)
}
- if n != size {
- panic(fmt.Sprintf("%d < %d", n, size))
+ if pos != req.Offset {
+ panic("seek failed")
}
+ resp.Data = resp.Data[:req.Size]
+ readDone := make(chan struct{})
+ ctx, cancel := context.WithCancel(ctx)
+ var readErr error
+ go func() {
+ defer close(readDone)
+ me.fn.FS.mu.Lock()
+ me.fn.FS.blockedReads++
+ me.fn.FS.event.Broadcast()
+ me.fn.FS.mu.Unlock()
+ var n int
+ r := missinggo.ContextedReader{r, ctx}
+ // log.Printf("reading %v bytes at %v", len(resp.Data), req.Offset)
+ if true {
+ // A user reported on that on freebsd 12.2, the system requires that reads are
+ // completely filled. Their system only asks for 64KiB at a time. I've seen systems that
+ // can demand up to 16MiB at a time, so this gets tricky. For now, I'll restore the old
+ // behaviour from before 2a7352a, which nobody reported problems with.
+ n, readErr = io.ReadFull(r, resp.Data)
+ } else {
+ n, readErr = r.Read(resp.Data)
+ if readErr == io.EOF {
+ readErr = nil
+ }
+ }
+ resp.Data = resp.Data[:n]
+ }()
+ defer func() {
+ <-readDone
+ me.fn.FS.mu.Lock()
+ me.fn.FS.blockedReads--
+ me.fn.FS.event.Broadcast()
+ me.fn.FS.mu.Unlock()
+ }()
+ defer cancel()
+
+ select {
+ case <-readDone:
+ return readErr
+ case <-me.fn.FS.destroyed:
+ return fuse.EIO
+ case <-ctx.Done():
+ return fuse.EINTR
+ }
+}
+
+func (me fileHandle) Release(context.Context, *fuse.ReleaseRequest) error {
return nil
}