]> Sergey Matveev's repositories - btrtrc.git/blob - fs/file_handle.go
Maintain a torrent.Reader for each file handle
[btrtrc.git] / fs / file_handle.go
1 package torrentfs
2
3 import (
4         "context"
5         "io"
6         "os"
7
8         "github.com/anacrolix/missinggo"
9         "github.com/anacrolix/torrent"
10
11         "bazil.org/fuse"
12         "bazil.org/fuse/fs"
13 )
14
15 type fileHandle struct {
16         fn fileNode
17         r  *torrent.Reader
18 }
19
20 var _ interface {
21         fs.HandleReader
22         fs.HandleReleaser
23 } = fileHandle{}
24
25 func (me fileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
26         torrentfsReadRequests.Add(1)
27         if req.Dir {
28                 panic("read on directory")
29         }
30         pos, err := me.r.Seek(me.fn.TorrentOffset+req.Offset, os.SEEK_SET)
31         if err != nil {
32                 panic(err)
33         }
34         if pos != me.fn.TorrentOffset+req.Offset {
35                 panic("seek failed")
36         }
37         resp.Data = resp.Data[:req.Size]
38         readDone := make(chan struct{})
39         ctx, cancel := context.WithCancel(ctx)
40         var readErr error
41         go func() {
42                 defer close(readDone)
43                 me.fn.FS.mu.Lock()
44                 me.fn.FS.blockedReads++
45                 me.fn.FS.event.Broadcast()
46                 me.fn.FS.mu.Unlock()
47                 var n int
48                 r := missinggo.ContextedReader{me.r, ctx}
49                 n, readErr = r.Read(resp.Data)
50                 if readErr == io.EOF {
51                         readErr = nil
52                 }
53                 resp.Data = resp.Data[:n]
54         }()
55         defer func() {
56                 <-readDone
57                 me.fn.FS.mu.Lock()
58                 me.fn.FS.blockedReads--
59                 me.fn.FS.event.Broadcast()
60                 me.fn.FS.mu.Unlock()
61         }()
62         defer cancel()
63
64         select {
65         case <-readDone:
66                 return readErr
67         case <-me.fn.FS.destroyed:
68                 return fuse.EIO
69         case <-ctx.Done():
70                 return fuse.EINTR
71         }
72 }
73
74 func (me fileHandle) Release(context.Context, *fuse.ReleaseRequest) error {
75         return me.r.Close()
76 }