]> Sergey Matveev's repositories - btrtrc.git/blob - storage/piece_file.go
36fe0664351c91984f0f9995817a67657cabc903
[btrtrc.git] / storage / piece_file.go
1 package storage
2
3 import (
4         "errors"
5         "io"
6         "os"
7         "path"
8
9         "github.com/anacrolix/missinggo"
10
11         "github.com/anacrolix/torrent/metainfo"
12 )
13
14 type pieceFileStorage struct {
15         fs missinggo.FileStore
16 }
17
18 func NewFileStorePieces(fs missinggo.FileStore) Client {
19         return &pieceFileStorage{
20                 fs: fs,
21         }
22 }
23
24 type pieceFileTorrentStorage struct {
25         s *pieceFileStorage
26 }
27
28 func (s *pieceFileStorage) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (Torrent, error) {
29         return &pieceFileTorrentStorage{s}, nil
30 }
31
32 func (s *pieceFileTorrentStorage) Close() error {
33         return nil
34 }
35
36 func (s *pieceFileTorrentStorage) Piece(p metainfo.Piece) Piece {
37         return pieceFileTorrentStoragePiece{s, p, s.s.fs}
38 }
39
40 type pieceFileTorrentStoragePiece struct {
41         ts *pieceFileTorrentStorage
42         p  metainfo.Piece
43         fs missinggo.FileStore
44 }
45
46 func (s pieceFileTorrentStoragePiece) completedPath() string {
47         return path.Join("completed", s.p.Hash().HexString())
48 }
49
50 func (s pieceFileTorrentStoragePiece) incompletePath() string {
51         return path.Join("incomplete", s.p.Hash().HexString())
52 }
53
54 func (s pieceFileTorrentStoragePiece) GetIsComplete() bool {
55         fi, err := s.fs.Stat(s.completedPath())
56         return err == nil && fi.Size() == s.p.Length()
57 }
58
59 func (s pieceFileTorrentStoragePiece) MarkComplete() error {
60         return s.fs.Rename(s.incompletePath(), s.completedPath())
61 }
62
63 func (s pieceFileTorrentStoragePiece) openFile() (f missinggo.File, err error) {
64         f, err = s.fs.OpenFile(s.completedPath(), os.O_RDONLY)
65         if err == nil {
66                 var fi os.FileInfo
67                 fi, err = f.Stat()
68                 if err == nil && fi.Size() == s.p.Length() {
69                         return
70                 }
71                 f.Close()
72         } else if !os.IsNotExist(err) {
73                 return
74         }
75         f, err = s.fs.OpenFile(s.incompletePath(), os.O_RDONLY)
76         if os.IsNotExist(err) {
77                 err = io.ErrUnexpectedEOF
78         }
79         return
80 }
81
82 func (s pieceFileTorrentStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
83         f, err := s.openFile()
84         if err != nil {
85                 return
86         }
87         defer f.Close()
88         missinggo.LimitLen(&b, s.p.Length()-off)
89         n, err = f.ReadAt(b, off)
90         off += int64(n)
91         if off >= s.p.Length() {
92                 err = io.EOF
93         } else if err == io.EOF {
94                 err = io.ErrUnexpectedEOF
95         }
96         return
97 }
98
99 func (s pieceFileTorrentStoragePiece) WriteAt(b []byte, off int64) (n int, err error) {
100         if s.GetIsComplete() {
101                 err = errors.New("piece completed")
102                 return
103         }
104         f, err := s.fs.OpenFile(s.incompletePath(), os.O_WRONLY|os.O_CREATE)
105         if err != nil {
106                 return
107         }
108         defer f.Close()
109         missinggo.LimitLen(&b, s.p.Length()-off)
110         return f.WriteAt(b, off)
111 }