8 "github.com/anacrolix/missinggo"
9 _ "github.com/mattn/go-sqlite3"
11 "github.com/anacrolix/torrent/metainfo"
14 // File-based storage for torrents, that isn't yet bound to a particular
16 type fileStorage struct {
20 func NewFile(baseDir string) ClientImpl {
26 func (fs *fileStorage) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
27 return &fileTorrentStorage{
31 pieceCompletionForDir(fs.baseDir),
35 // File-based torrent storage, not yet bound to a Torrent.
36 type fileTorrentStorage struct {
39 infoHash metainfo.Hash
40 completion pieceCompletion
43 func (fts *fileTorrentStorage) Piece(p metainfo.Piece) PieceImpl {
44 // Create a view onto the file-based torrent storage.
45 _io := fileStorageTorrent{fts}
46 // Return the appropriate segments of this.
47 return &fileStoragePiece{
50 missinggo.NewSectionWriter(_io, p.Offset(), p.Length()),
51 io.NewSectionReader(_io, p.Offset(), p.Length()),
55 func (fs *fileTorrentStorage) Close() error {
60 // Exposes file-based storage of a torrent, as one big ReadWriterAt.
61 type fileStorageTorrent struct {
62 fts *fileTorrentStorage
65 // Returns EOF on short or missing file.
66 func (fst *fileStorageTorrent) readFileAt(fi metainfo.FileInfo, b []byte, off int64) (n int, err error) {
67 f, err := os.Open(fst.fts.fileInfoName(fi))
68 if os.IsNotExist(err) {
69 // File missing is treated the same as a short file.
77 // Limit the read to within the expected bounds of this file.
78 if int64(len(b)) > fi.Length-off {
81 for off < fi.Length && len(b) != 0 {
82 n1, err1 := f.ReadAt(b, off)
94 // Only returns EOF at the end of the torrent. Premature EOF is ErrUnexpectedEOF.
95 func (fst fileStorageTorrent) ReadAt(b []byte, off int64) (n int, err error) {
96 for _, fi := range fst.fts.info.UpvertedFiles() {
98 n1, err1 := fst.readFileAt(fi, b, off)
113 err = io.ErrUnexpectedEOF
123 func (fst fileStorageTorrent) WriteAt(p []byte, off int64) (n int, err error) {
124 for _, fi := range fst.fts.info.UpvertedFiles() {
125 if off >= fi.Length {
130 if int64(n1) > fi.Length-off {
131 n1 = int(fi.Length - off)
133 name := fst.fts.fileInfoName(fi)
134 os.MkdirAll(filepath.Dir(name), 0770)
136 f, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0660)
140 n1, err = f.WriteAt(p[:n1], off)
155 func (fts *fileTorrentStorage) fileInfoName(fi metainfo.FileInfo) string {
156 return filepath.Join(append([]string{fts.fs.baseDir, fts.info.Name}, fi.Path...)...)