github.com/anacrolix/torrent@v1.61.0/fs/file_handle.go (about)

     1  //go:build !windows
     2  
     3  package torrentfs
     4  
     5  import (
     6  	"context"
     7  	"io"
     8  
     9  	"github.com/anacrolix/fuse"
    10  	"github.com/anacrolix/fuse/fs"
    11  	"github.com/anacrolix/missinggo/v2"
    12  
    13  	"github.com/anacrolix/torrent"
    14  )
    15  
    16  type fileHandle struct {
    17  	fn fileNode
    18  	tf *torrent.File
    19  }
    20  
    21  var _ interface {
    22  	fs.HandleReader
    23  	fs.HandleReleaser
    24  } = fileHandle{}
    25  
    26  func (me fileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
    27  	torrentfsReadRequests.Add(1)
    28  	if req.Dir {
    29  		panic("read on directory")
    30  	}
    31  	r := me.tf.NewReader()
    32  	defer r.Close()
    33  	pos, err := r.Seek(req.Offset, io.SeekStart)
    34  	if err != nil {
    35  		panic(err)
    36  	}
    37  	if pos != req.Offset {
    38  		panic("seek failed")
    39  	}
    40  	resp.Data = resp.Data[:req.Size]
    41  	readDone := make(chan struct{})
    42  	ctx, cancel := context.WithCancel(ctx)
    43  	var readErr error
    44  	go func() {
    45  		defer close(readDone)
    46  		me.fn.FS.mu.Lock()
    47  		me.fn.FS.blockedReads++
    48  		me.fn.FS.event.Broadcast()
    49  		me.fn.FS.mu.Unlock()
    50  		var n int
    51  		r := missinggo.ContextedReader{r, ctx}
    52  		// log.Printf("reading %v bytes at %v", len(resp.Data), req.Offset)
    53  		if true {
    54  			// A user reported on that on freebsd 12.2, the system requires that reads are
    55  			// completely filled. Their system only asks for 64KiB at a time. I've seen systems that
    56  			// can demand up to 16MiB at a time, so this gets tricky. For now, I'll restore the old
    57  			// behaviour from before 2a7352a, which nobody reported problems with.
    58  			n, readErr = io.ReadFull(r, resp.Data)
    59  			if readErr == io.ErrUnexpectedEOF {
    60  				readErr = nil
    61  			}
    62  		} else {
    63  			n, readErr = r.Read(resp.Data)
    64  			if readErr == io.EOF {
    65  				readErr = nil
    66  			}
    67  		}
    68  		resp.Data = resp.Data[:n]
    69  	}()
    70  	defer func() {
    71  		<-readDone
    72  		me.fn.FS.mu.Lock()
    73  		me.fn.FS.blockedReads--
    74  		me.fn.FS.event.Broadcast()
    75  		me.fn.FS.mu.Unlock()
    76  	}()
    77  	defer cancel()
    78  
    79  	select {
    80  	case <-readDone:
    81  		return readErr
    82  	case <-me.fn.FS.destroyed:
    83  		return fuse.EIO
    84  	case <-ctx.Done():
    85  		return fuse.EINTR
    86  	}
    87  }
    88  
    89  func (me fileHandle) Release(context.Context, *fuse.ReleaseRequest) error {
    90  	return nil
    91  }