metainfoFileInfos,
info.FileSegmentsIndex(),
infoHash,
+ classicFileIo{},
fs,
}
if t.partFiles() {
--- /dev/null
+package storage
+
+import (
+ "errors"
+ "io"
+ "io/fs"
+ "os"
+ "path/filepath"
+)
+
+type fileWriter interface {
+ io.WriterAt
+ io.Closer
+}
+
+type fileReader interface {
+ // Seeks to the next data in the file. If hole-seeking/sparse-files are not supported, should
+ // seek to the offset.
+ seekData(offset int64) (ret int64, err error)
+ io.WriterTo
+ io.ReadCloser
+}
+
+type fileIo interface {
+ openForSharedRead(name string) (sharedFileIf, error)
+ openForRead(name string) (fileReader, error)
+ openForWrite(name string) (fileWriter, error)
+}
+
+type classicFileIo struct{}
+
+func (i classicFileIo) openForSharedRead(name string) (sharedFileIf, error) {
+ return sharedFiles.Open(name)
+}
+
+type classicFileReader struct {
+ *os.File
+}
+
+func (c classicFileReader) seekData(offset int64) (ret int64, err error) {
+ return seekData(c.File, offset)
+}
+
+func (i classicFileIo) openForRead(name string) (fileReader, error) {
+ f, err := os.Open(name)
+ return classicFileReader{f}, err
+}
+
+func (classicFileIo) openForWrite(p string) (f fileWriter, err error) {
+ f, err = os.OpenFile(p, os.O_WRONLY|os.O_CREATE, filePerm)
+ if err == nil {
+ return
+ }
+ if errors.Is(err, fs.ErrNotExist) {
+ err = os.MkdirAll(filepath.Dir(p), dirPerm)
+ if err != nil {
+ return
+ }
+ } else if errors.Is(err, fs.ErrPermission) {
+ err = os.Chmod(p, filePerm)
+ if err != nil {
+ return
+ }
+ } else {
+ return
+ }
+ f, err = os.OpenFile(p, os.O_WRONLY|os.O_CREATE, filePerm)
+ return
+}
return
}
file := me.t.file(fileIndex)
- var f *os.File
+ // Do we want io.WriterTo here, or are we happy to let that be type asserted in io.CopyN?
+ var f fileReader
f, err = me.t.openFile(file)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
panicif.GreaterThan(extent.End(), file.FileInfo.Length)
extentRemaining := extent.Length
var dataOffset int64
- dataOffset, err = seekData(f, extent.Start)
+ dataOffset, err = f.seekData(extent.Start)
if err == io.EOF {
return
}
"errors"
"io"
"io/fs"
- "os"
- "path/filepath"
"github.com/anacrolix/missinggo/v2/panicif"
"github.com/anacrolix/torrent/segments"
return
}
-func (fst fileTorrentImplIO) openForWrite(file file) (f *os.File, err error) {
- // It might be possible to have a writable handle shared files cache if we need it.
- fst.fts.logger().Debug("openForWrite", "file.safeOsPath", file.safeOsPath)
- p := fst.fts.pathForWrite(&file)
- f, err = os.OpenFile(p, os.O_WRONLY|os.O_CREATE, filePerm)
- if err == nil {
- return
- }
- if errors.Is(err, fs.ErrNotExist) {
- err = os.MkdirAll(filepath.Dir(p), dirPerm)
- if err != nil {
- return
- }
- } else if errors.Is(err, fs.ErrPermission) {
- err = os.Chmod(p, filePerm)
- if err != nil {
- return
- }
- } else {
- return
- }
- f, err = os.OpenFile(p, os.O_WRONLY|os.O_CREATE, filePerm)
- return
-}
-
func (fst fileTorrentImplIO) WriteAt(p []byte, off int64) (n int, err error) {
for i, e := range fst.fts.segmentLocater.LocateIter(
segments.Extent{off, int64(len(p))},
) {
- var f *os.File
- f, err = fst.openForWrite(fst.fts.file(i))
+ var f fileWriter
+ f, err = fst.fts.openForWrite(fst.fts.file(i))
if err != nil {
return
}
metainfoFileInfos []metainfo.FileInfo
segmentLocater segments.Index
infoHash metainfo.Hash
+ io fileIo
// Save memory by pointing to the other data.
client *fileClientImpl
}
// Fine to open once under each name on a unix system. We could make the shared file keys more
// constrained, but it shouldn't matter. TODO: Ensure at most one of the names exist.
if me.partFiles() {
- f, err = sharedFiles.Open(file.partFilePath())
+ f, err = me.io.openForSharedRead(file.partFilePath())
}
if err == nil && f == nil || errors.Is(err, fs.ErrNotExist) {
- f, err = sharedFiles.Open(file.safeOsPath)
+ f, err = me.io.openForSharedRead(file.safeOsPath)
}
file.mu.RUnlock()
return
}
-// Open file for reading.
-func (me *fileTorrentImpl) openFile(file file) (f *os.File, err error) {
+// Open file for reading. Not a shared handle if that matters.
+func (me *fileTorrentImpl) openFile(file file) (f fileReader, err error) {
file.mu.RLock()
// Fine to open once under each name on a unix system. We could make the shared file keys more
// constrained, but it shouldn't matter. TODO: Ensure at most one of the names exist.
if me.partFiles() {
- f, err = os.Open(file.partFilePath())
+ f, err = me.io.openForRead(file.partFilePath())
}
if err == nil && f == nil || errors.Is(err, fs.ErrNotExist) {
- f, err = os.Open(file.safeOsPath)
+ f, err = me.io.openForRead(file.safeOsPath)
}
file.mu.RUnlock()
return
}
+
+func (fst fileTorrentImpl) openForWrite(file file) (_ fileWriter, err error) {
+ // It might be possible to have a writable handle shared files cache if we need it.
+ fst.logger().Debug("openForWrite", "file.safeOsPath", file.safeOsPath)
+ return fst.io.openForWrite(fst.pathForWrite(&file))
+}