github.com/anacrolix/torrent@v1.61.0/storage/file-torrent-io.go (about) 1 package storage 2 3 import ( 4 "errors" 5 "io" 6 "io/fs" 7 8 "github.com/anacrolix/missinggo/v2/panicif" 9 10 "github.com/anacrolix/torrent/segments" 11 ) 12 13 // Exposes file-based storage of a torrent, as one big ReadWriterAt. 14 type fileTorrentImplIO struct { 15 fts *fileTorrentImpl 16 } 17 18 // Returns EOF on short or missing file. 19 func (fst fileTorrentImplIO) readFileAt(file file, b []byte, off int64) (n int, err error) { 20 fst.fts.logger().Debug("readFileAt", "file.safeOsPath", file.safeOsPath) 21 f, err := fst.fts.openSharedFile(file) 22 if errors.Is(err, fs.ErrNotExist) { 23 // File missing is treated the same as a short file. Should we propagate this through the 24 // interface now that fs.ErrNotExist is a thing? 25 err = io.EOF 26 return 27 } 28 if err != nil { 29 return 30 } 31 defer f.Close() 32 // Limit the read to within the expected bounds of this file. 33 if int64(len(b)) > file.length()-off { 34 b = b[:file.length()-off] 35 } 36 for off < file.length() && len(b) != 0 { 37 n1, err1 := f.ReadAt(b, off) 38 b = b[n1:] 39 n += n1 40 off += int64(n1) 41 if n1 == 0 { 42 err = err1 43 break 44 } 45 } 46 return 47 } 48 49 // Only returns EOF at the end of the torrent. Premature EOF is ErrUnexpectedEOF. 50 func (fst fileTorrentImplIO) ReadAt(b []byte, off int64) (n int, err error) { 51 for i, e := range fst.fts.segmentLocater.LocateIter( 52 segments.Extent{off, int64(len(b))}, 53 ) { 54 n1, err1 := fst.readFileAt(fst.fts.file(i), b[:e.Length], e.Start) 55 n += n1 56 b = b[n1:] 57 if segments.Int(n1) == e.Length { 58 switch err1 { 59 // ReaderAt.ReadAt contract. 60 case nil, io.EOF: 61 default: 62 err = err1 63 return 64 } 65 } else { 66 panicif.Nil(err1) 67 err = err1 68 return 69 } 70 } 71 if len(b) != 0 { 72 // We're at the end of the torrent. 73 err = io.EOF 74 } 75 return 76 } 77 78 func (fst fileTorrentImplIO) WriteAt(p []byte, off int64) (n int, err error) { 79 for i, e := range fst.fts.segmentLocater.LocateIter( 80 segments.Extent{off, int64(len(p))}, 81 ) { 82 var f fileWriter 83 f, err = fst.fts.openForWrite(fst.fts.file(i)) 84 if err != nil { 85 return 86 } 87 var n1 int 88 n1, err = f.WriteAt(p[:e.Length], e.Start) 89 closeErr := f.Close() 90 n += n1 91 p = p[n1:] 92 if err == nil { 93 err = closeErr 94 } 95 if err == nil && int64(n1) != e.Length { 96 err = io.ErrShortWrite 97 } 98 if err != nil { 99 return 100 } 101 } 102 return 103 }