14 dataPkg "bitbucket.org/anacrolix/go.torrent/data"
15 "github.com/anacrolix/libtorgo/metainfo"
26 completed map[string]struct{}
29 func (me *store) OpenTorrent(info *metainfo.Info) dataPkg.Data {
30 return &data{info, me}
33 type StoreOption func(*store)
35 func Capacity(bytes int64) StoreOption {
36 return func(s *store) {
41 func NewStore(baseDir string, opt ...StoreOption) dataPkg.Store {
42 s := &store{baseDir, -1, nil}
43 for _, o := range opt {
50 func (me *store) initCompleted() {
51 fis, err := me.readCompletedDir()
55 me.completed = make(map[string]struct{}, len(fis))
56 for _, fi := range fis {
57 me.completed[fi.Name()] = struct{}{}
61 func (me *store) completePieceDirPath() string {
62 return filepath.Join(me.baseDir, "complete")
65 func (me *store) path(p metainfo.Piece, completed bool) string {
66 return filepath.Join(me.baseDir, func() string {
72 }(), fmt.Sprintf("%x", p.Hash()))
75 func (me *store) pieceComplete(p metainfo.Piece) bool {
76 _, ok := me.completed[hex.EncodeToString(p.Hash())]
80 func (me *store) pieceWrite(p metainfo.Piece) (f *os.File) {
81 if me.pieceComplete(p) {
84 name := me.path(p, false)
85 os.MkdirAll(filepath.Dir(name), dirPerm)
86 f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY, filePerm)
93 func (me *store) pieceRead(p metainfo.Piece) (f *os.File) {
94 f, err := os.Open(me.path(p, true))
98 if !os.IsNotExist(err) {
101 f, err = os.Open(me.path(p, false))
105 if !os.IsNotExist(err) {
111 func (me *store) readCompletedDir() (fis []os.FileInfo, err error) {
112 f, err := os.Open(me.completePieceDirPath())
114 if os.IsNotExist(err) {
119 fis, err = f.Readdir(-1)
124 func (me *store) removeCompleted(name string) (err error) {
125 err = os.Remove(filepath.Join(me.completePieceDirPath(), name))
126 if os.IsNotExist(err) {
132 delete(me.completed, name)
136 func (me *store) makeSpace(space int64) error {
140 if space > me.capacity {
141 return errors.New("space requested exceeds capacity")
143 fis, err := me.readCompletedDir()
148 for _, fi := range fis {
151 for size > me.capacity-space {
152 i := rand.Intn(len(fis))
153 me.removeCompleted(fis[i].Name())
154 size -= fis[i].Size()
155 fis[i] = fis[len(fis)-1]
156 fis = fis[:len(fis)-1]
161 func (me *store) PieceCompleted(p metainfo.Piece) (err error) {
162 err = me.makeSpace(p.Length())
167 incompletePiecePath = me.path(p, false)
168 completedPiecePath = me.path(p, true)
170 fSrc, err := os.Open(incompletePiecePath)
175 os.MkdirAll(filepath.Dir(completedPiecePath), dirPerm)
176 fDst, err := os.OpenFile(completedPiecePath, os.O_EXCL|os.O_CREATE|os.O_WRONLY, filePerm)
182 r := io.TeeReader(io.LimitReader(fSrc, p.Length()), hasher)
183 _, err = io.Copy(fDst, r)
187 if !bytes.Equal(hasher.Sum(nil), p.Hash()) {
188 err = errors.New("piece incomplete")
189 os.Remove(completedPiecePath)
192 os.Remove(incompletePiecePath)
193 me.completed[hex.EncodeToString(p.Hash())] = struct{}{}