package node import ( "context" "io" "log" "math" "syscall" "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" ) var _ fs.NodeGetattrer = &File{} var _ fs.NodeOpener = &File{} var _ fs.NodeReader = &File{} var _ fs.NodeReleaser = &File{} // File is a fuse node for files inside a torrent type File struct { fs.Inode f ReaderFunc r io.ReaderAt len int64 pieceLen int32 numPieces int64 } func NewFile(readerFunc ReaderFunc, len int64) *File { return &File{ f: readerFunc, len: len, } } func NewFileWithBlocks(readerFunc ReaderFunc, len int64, pieceLen int32, numPieces int64) *File { return &File{ f: readerFunc, len: len, pieceLen: pieceLen, numPieces: numPieces, } } func (tr *File) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno { out.Mode = syscall.S_IFREG & 07777 out.Nlink = 1 out.Size = uint64(tr.len) if tr.pieceLen != 0 { out.Blksize = uint32(tr.pieceLen) out.Blocks = uint64(tr.numPieces) } return fs.OK } func (tr *File) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { // TODO use filehandle if tr.r == nil { r, err := tr.f() if err != nil { log.Println("error opening reader for file", err) return nil, 0, syscall.EIO } tr.r = r } return nil, fuse.FOPEN_KEEP_CACHE, fs.OK } func (tr *File) Read(ctx context.Context, f fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) { end := int(math.Min(float64(len(dest)), float64(int64(tr.len)-off))) if end < 0 { end = 0 } buf := dest[:end] n, err := tr.r.ReadAt(buf, off) if err != nil && err != io.EOF { log.Println("error read data", err) return nil, syscall.EIO } buf = buf[:n] return fuse.ReadResultData(buf), fs.OK } func (tr *File) Release(ctx context.Context, f fs.FileHandle) syscall.Errno { log.Println("closing file...") if tr.r != nil { closer, ok := tr.r.(io.Closer) if ok { if err := closer.Close(); err != nil { log.Println("error closing file", err) return syscall.EIO } } else { log.Println("file is not implementing close method") } } return fs.OK }