]> Sergey Matveev's repositories - btrtrc.git/blob - storage/file.go
New storage interface
[btrtrc.git] / storage / file.go
1 package storage
2
3 import (
4         "io"
5         "os"
6         "path/filepath"
7
8         "github.com/anacrolix/missinggo"
9
10         "github.com/anacrolix/torrent/metainfo"
11 )
12
13 type fileStorage struct {
14         baseDir   string
15         completed map[[20]byte]bool
16 }
17
18 func NewFile(baseDir string) *fileStorage {
19         return &fileStorage{
20                 baseDir: baseDir,
21         }
22 }
23
24 func (me *fileStorage) Piece(p metainfo.Piece) Piece {
25         _io := &fileStorageTorrent{
26                 p.Info,
27                 me.baseDir,
28         }
29         return &fileStoragePiece{
30                 me,
31                 p,
32                 missinggo.NewSectionWriter(_io, p.Offset(), p.Length()),
33                 io.NewSectionReader(_io, p.Offset(), p.Length()),
34         }
35 }
36
37 type fileStoragePiece struct {
38         *fileStorage
39         p metainfo.Piece
40         io.WriterAt
41         io.ReaderAt
42 }
43
44 func (me *fileStoragePiece) GetIsComplete() bool {
45         return me.completed[me.p.Hash()]
46 }
47
48 func (me *fileStoragePiece) MarkComplete() error {
49         if me.completed == nil {
50                 me.completed = make(map[[20]byte]bool)
51         }
52         me.completed[me.p.Hash()] = true
53         return nil
54 }
55
56 type fileStorageTorrent struct {
57         info    *metainfo.Info
58         baseDir string
59 }
60
61 // Returns EOF on short or missing file.
62 func (me *fileStorageTorrent) readFileAt(fi metainfo.FileInfo, b []byte, off int64) (n int, err error) {
63         f, err := os.Open(me.fileInfoName(fi))
64         if os.IsNotExist(err) {
65                 // File missing is treated the same as a short file.
66                 err = io.EOF
67                 return
68         }
69         if err != nil {
70                 return
71         }
72         defer f.Close()
73         // Limit the read to within the expected bounds of this file.
74         if int64(len(b)) > fi.Length-off {
75                 b = b[:fi.Length-off]
76         }
77         for off < fi.Length && len(b) != 0 {
78                 n1, err1 := f.ReadAt(b, off)
79                 b = b[n1:]
80                 n += n1
81                 off += int64(n1)
82                 if n1 == 0 {
83                         err = err1
84                         break
85                 }
86         }
87         return
88 }
89
90 // Only returns EOF at the end of the torrent. Premature EOF is ErrUnexpectedEOF.
91 func (me *fileStorageTorrent) ReadAt(b []byte, off int64) (n int, err error) {
92         for _, fi := range me.info.UpvertedFiles() {
93                 for off < fi.Length {
94                         n1, err1 := me.readFileAt(fi, b, off)
95                         n += n1
96                         off += int64(n1)
97                         b = b[n1:]
98                         if len(b) == 0 {
99                                 // Got what we need.
100                                 return
101                         }
102                         if n1 != 0 {
103                                 // Made progress.
104                                 continue
105                         }
106                         err = err1
107                         if err == io.EOF {
108                                 // Lies.
109                                 err = io.ErrUnexpectedEOF
110                         }
111                         return
112                 }
113                 off -= fi.Length
114         }
115         err = io.EOF
116         return
117 }
118
119 func (me *fileStorageTorrent) WriteAt(p []byte, off int64) (n int, err error) {
120         for _, fi := range me.info.UpvertedFiles() {
121                 if off >= fi.Length {
122                         off -= fi.Length
123                         continue
124                 }
125                 n1 := len(p)
126                 if int64(n1) > fi.Length-off {
127                         n1 = int(fi.Length - off)
128                 }
129                 name := me.fileInfoName(fi)
130                 os.MkdirAll(filepath.Dir(name), 0770)
131                 var f *os.File
132                 f, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0660)
133                 if err != nil {
134                         return
135                 }
136                 n1, err = f.WriteAt(p[:n1], off)
137                 f.Close()
138                 if err != nil {
139                         return
140                 }
141                 n += n1
142                 off = 0
143                 p = p[n1:]
144                 if len(p) == 0 {
145                         break
146                 }
147         }
148         return
149 }
150
151 func (me *fileStorageTorrent) fileInfoName(fi metainfo.FileInfo) string {
152         return filepath.Join(append([]string{me.baseDir, me.info.Name}, fi.Path...)...)
153 }