1 /* This file is part of pr-downloader (GPL v2 or later), see the LICENSE file */
2
3 #include "File.h"
4 #include "FileSystem.h"
5 #include "Logger.h"
6 #include "IHash.h"
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <stdlib.h>
14 #include <sys/time.h>
15
16 #ifdef WIN32
17 #include <windows.h>
18 #endif
19
CFile()20 CFile::CFile():
21 handle(NULL),
22 piecesize(-1),
23 size(-1),
24 curpos(0),
25 isnewfile(true),
26 timestamp(0)
27 {
28 }
29
~CFile()30 CFile::~CFile()
31 {
32 //TODO: write buffered data
33 Close();
34 }
Close()35 void CFile::Close()
36 {
37 if (handle!=0) {
38 fclose(handle);
39 if (IsNewFile()) {
40 unlink(filename.c_str()); //delete possible existing destination file
41 rename(tmpfile.c_str(), filename.c_str());
42 isnewfile = false;
43 }
44 handle=0;
45 }
46 }
47
Open(const std::string & filename,long size,int piecesize)48 bool CFile::Open(const std::string& filename, long size, int piecesize)
49 {
50 LOG_DEBUG("%s %d %d", filename.c_str(), size, piecesize);
51 this->filename=filename;
52 this->size=size;
53 fileSystem->createSubdirs(filename);
54 SetPieceSize(piecesize);
55 // fileSystem->createSubdirs(filename);
56 if (handle!=NULL) {
57 LOG_ERROR("file opened before old was closed");
58 return false;
59 }
60 struct stat sb;
61 int res=stat(filename.c_str(), &sb);
62 timestamp = 0;
63 isnewfile=res!=0;
64 if (isnewfile) { //if file is new, create it, if not, open the existing one without truncating it
65 tmpfile = filename + ".tmp";
66 handle=fopen(tmpfile.c_str(), "wb+");
67 } else {
68 handle=fopen(filename.c_str(), "rb+");
69 timestamp = sb.st_mtime;
70 }
71 if (handle==0) {
72 LOG_ERROR("open(%s): %s",filename.c_str(), strerror(errno));
73 return false;
74 }
75
76 if((!isnewfile) && (size>0) && (size!=sb.st_size)) { //truncate file if real-size != excepted file size
77 int ret=ftruncate(fileno(handle), size);
78 if (ret!=0) {
79 LOG_ERROR("ftruncate failed");
80 }
81 LOG_ERROR("File already exists but file-size missmatched");
82 } else if (size<=0) {
83 //TODO: allocate disk space
84 }
85 LOG_INFO("opened %s", filename.c_str());
86 return true;
87 }
88
Hash(IHash & hash,int piece)89 bool CFile::Hash(IHash& hash, int piece)
90 {
91 // LOG("Hash() piece %d", piece);
92 char buf[IO_BUF_SIZE];
93 SetPos(0, piece);
94 hash.Init();
95 // LOG("piece %d left: %d",piece, GetPieceSize(piece));
96 int read=0;
97 long unsigned left=GetPieceSize(piece); //total bytes to hash
98 if (left==0) {
99 LOG_ERROR("tried to hash empty piece %d", piece);
100 return false;
101 }
102
103 while(left>0) {
104 int toread=std::min(left, (long unsigned)sizeof(buf));
105 read=Read(buf, toread, piece);
106 if(read<=0) {
107 LOG_ERROR("EOF or read error on piece %d, left: %d toread: %d size: %d, GetPiecePos %d GetPieceSize(): %d read: %d", piece, left, toread, GetPieceSize(piece), GetPiecePos(piece), GetPieceSize(piece), read);
108 LOG_ERROR("curpos: %d", curpos);
109 return false;
110 }
111 hash.Update(buf, toread);
112 left=left-toread;
113 }
114 hash.Final();
115 SetPos(0, piece);
116 // LOG("CFile::Hash(): %s piece:%d", hash.toString().c_str(), piece);
117 return true;
118 }
119
Read(char * buf,int bufsize,int piece)120 int CFile::Read(char *buf, int bufsize, int piece)
121 {
122 SetPos(GetPiecePos(piece), piece);
123 // LOG("Read(%d) bufsize: %d GetPiecePos(): %d GetPieceSize() %d",piece, bufsize, GetPiecePos(piece), GetPieceSize(piece));
124 clearerr(handle);
125 int items=fread(buf, bufsize, 1, handle);
126 if (items<=0) {
127 if(ferror(handle)) {
128 LOG_ERROR("read error %s bufsize: %d curpos: %d GetPieceSize: %d", strerror(errno), bufsize, curpos, GetPieceSize());
129 SetPos(0, piece);
130 return -1;
131 }
132 if(feof(handle)) {
133 LOG_ERROR("EOF while Read: '%s' items: %d!", strerror(errno), items);
134 LOG_ERROR("read error %s bufsize: %d curpos: %d GetPieceSize: %d", strerror(errno), bufsize, curpos, GetPieceSize());
135 return -1;
136 }
137 }
138 SetPos(GetPiecePos(piece)+bufsize, piece); //inc pos
139 return bufsize;
140 }
141
SetPos(long pos,int piece)142 void CFile::SetPos(long pos, int piece)
143 {
144 // LOG("SetPos() pos %d piece%d", pos, piece);
145 if (piece>=0) {
146 //LOG_DEBUG("pos: %d piecesize: %d", pos, piecesize);
147 assert(pieces[piece].pos<=size+pos);
148 //assert(pos<=piecesize); This is no longer needed since we download multiple pieces together
149 pieces[piece].pos =pos;
150 } else {
151 assert(size<=0 || pos<=size);
152 curpos = pos;
153 }
154 Seek(pos, piece);
155 }
156
Write(const char * buf,int bufsize,int piece)157 int CFile::Write(const char*buf, int bufsize, int piece)
158 {
159 SetPos(GetPiecePos(piece), piece);
160 clearerr(handle);
161 // LOG("Write() bufsize %d piece %d handle %d", bufsize, piece, fileno(handle));
162 int res=fwrite(buf, bufsize, 1, handle);
163 if (res!=1)
164 LOG_ERROR("write error %d", res);
165 // LOG("wrote bufsize %d", bufsize);
166 if(ferror(handle)!=0) {
167 LOG_ERROR("Error in write(): %s", strerror(errno));
168 abort();
169 }
170 if(feof(handle)) {
171 LOG_ERROR("EOF in write(): %s", strerror(errno));
172 }
173 SetPos(GetPiecePos(piece)+bufsize, piece);
174 /* if ((piece>=0) && (GetPiecePos(piece)==GetPieceSize(piece))) {
175 LOG("piece finished: %d", piece);
176 }
177 */
178 return bufsize;
179 }
180
181
Seek(unsigned long pos,int piece)182 int CFile::Seek(unsigned long pos, int piece)
183 {
184 assert(piece<=(int)pieces.size());
185 if(piece>=0) { //adjust position relative to piece pos
186 pos=this->piecesize*piece+pos;
187 }
188 // LOG("Seek() pos: %d piece: %d", pos, piece);
189 clearerr(handle);
190 if (fseek(handle, pos, SEEK_SET)!=0) {
191 LOG_ERROR("seek error %ld", pos);
192 }
193 return pos;
194 }
195
SetPieceSize(int pieceSize)196 bool CFile::SetPieceSize(int pieceSize)
197 {
198 assert(handle==NULL); //this function has to be called before the file is opened
199 pieces.clear();
200 if ((size<=0) || (pieceSize<=0)) {
201 LOG_DEBUG("SetPieceSize(): FileSize:%ld PieceSize: %d", size, pieceSize);
202 return false;
203 }
204 if (size<pieceSize) {
205 pieceSize = size;
206 LOG_DEBUG("SetPieceSize(): forcing lower pieceSize: %d", pieceSize);
207 }
208 unsigned count=this->size/pieceSize;
209 if (count==pieces.size()) //check if size is already correct
210 return true;
211 pieces.clear();
212 if(this->size%pieceSize>0)
213 count++;
214 if(count==0) {
215 LOG_ERROR("SetPieceSize(): count==0");
216 return false;
217 }
218 for(unsigned i=0; i<count; i++) {
219 pieces.push_back(CFilePiece());
220 }
221 piecesize=pieceSize;
222 curpos=0;
223 // LOG("SetPieceSize piecesize: %d filesize: %ld pieces count:%d", pieceSize, this->size, (int)pieces.size());
224 return true;
225 }
GetPiecesSize(std::vector<unsigned int> pieces) const226 int CFile::GetPiecesSize(std::vector< unsigned int > pieces) const
227 {
228 int size = 0;
229 for ( std::vector< unsigned int >::iterator it = pieces.begin(); it != pieces.end(); it++ )
230 {
231 size += GetPieceSize(*it);
232 }
233 return size;
234
235 }
236
GetPieceSize(int piece) const237 int CFile::GetPieceSize(int piece) const
238 {
239 if (piece>=0) {
240 assert(piece<=(int)pieces.size());
241 if ((int)pieces.size()-1==piece) //last piece
242 return size-(piecesize*((int)pieces.size()-1));
243 // LOG("GetPieceSize piece %d, pieces.size() %d piecesize: %d size %d ", piece, pieces.size(),piecesize, size);
244 return piecesize;
245 }
246 if (size<0) {
247 return GetSizeFromHandle();
248 }
249 return size;
250 }
251
GetPiecePos(int piece) const252 long CFile::GetPiecePos(int piece) const
253 {
254 assert(piece<=(int)pieces.size());
255 if (piece>=0)
256 return pieces[piece].pos;
257 return curpos;
258 }
259
GetSizeFromHandle() const260 long CFile::GetSizeFromHandle() const
261 {
262 if(handle==NULL) {
263 LOG_ERROR("GetSize(): file isn't opened!");
264 return -1;
265 }
266
267 struct stat sb;
268 if (fstat(fileno(handle), &sb)!=0) {
269 LOG_ERROR("CFile::SetSize(): fstat failed");
270 return -1;
271 }
272 // LOG("GetSizeFromHandle: %d blocks: %d", sb.st_size, sb.st_blocks);
273 return sb.st_size;
274 }
275
IsNewFile()276 bool CFile::IsNewFile()
277 {
278 return isnewfile;
279 }
280
GetTimestamp()281 long CFile::GetTimestamp()
282 {
283 return timestamp;
284 }
285
SetTimestamp(long timestamp)286 bool CFile::SetTimestamp(long timestamp)
287 {
288 #ifdef WIN32
289 FILETIME ftime;
290 HANDLE h;
291 bool close = false;
292 if (handle==NULL) {
293 h = CreateFile(filename.c_str() , GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
294 close = true;
295 } else {
296 h = (HANDLE)_get_osfhandle(fileno(handle));
297 }
298 if (h == NULL) {
299 return false;
300 }
301 fileSystem->TimestampToFiletime(timestamp, ftime);
302 bool ret = SetFileTime(h, NULL, NULL, &ftime);
303 if (close) { //close opened file
304 CloseHandle(h);
305 }
306 return ret;
307 #else
308 struct timeval tv = {0, 0};
309 tv.tv_sec = timestamp;
310 if (handle==NULL) {
311 return lutimes(filename.c_str(), &tv) == 0;
312 } else {
313 return futimes(fileno(handle), &tv) == 0;
314 }
315 #endif
316 }
317