5 * Created by Victor Grishchenko on 10/6/09.
6 * Copyright 2009 Delft University of Technology. All rights reserved.
10 #include "compat/unixio.h"
18 #include "compat/util.h"
22 std::vector<FileTransfer*> FileTransfer::files(20);
24 int FileTransfer::instance = 0;
25 #define BINHASHSIZE (sizeof(bin64_t)+sizeof(Sha1Hash))
27 #include "ext/seq_picker.cpp"
29 // FIXME: separate Bootstrap() and Download(), then Size(), Progress(), SeqProgress()
31 FileTransfer::FileTransfer (const char* filename, const Sha1Hash& _root_hash) :
32 root_hash_(_root_hash), fd_(0), hashfd_(0), dry_run_(false),
33 peak_count_(0), hashes_(NULL), error_(NULL), size_(0), sizek_(0),
34 complete_(0), completek_(0), seq_complete_(0), hs_in_offset_(0)
36 fd_ = open(filename,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
39 if (files.size()<fd_+1)
42 if (root_hash_==Sha1Hash::ZERO) // fresh submit, hash it
46 picker_ = new SeqPiecePicker(this);
50 void FileTransfer::LoadPeaks () {
51 std::string file_name = GetTempFilename(root_hash_,instance,std::string(".peaks"));
52 int peakfd = open(file_name.c_str(),O_RDONLY,0);
57 while (sizeof(bin64_t)==read(peakfd,&peak,sizeof(bin64_t))) {
58 read(peakfd,hash,Sha1Hash::SIZE);
59 OfferPeak(peak, Sha1Hash(false,hash));
65 /** Basically, simulated receiving every single packet, except
66 for some optimizations. */
67 void FileTransfer::RecoverProgress () {
72 // at this point, we may use mmapd hashes already
73 // so, lets verify hashes and the data we've got
74 lseek(fd_,0,SEEK_SET);
75 for(int p=0; p<size_kilo(); p++) {
77 size_t rd = read(fd_,buf,1<<10);
78 OfferData(bin64_t(0,p), buf, rd);
86 void FileTransfer::SavePeaks () {
87 std::string file_name = GetTempFilename(root_hash_,instance,std::string(".peaks"));
88 int peakfd = open(file_name.c_str(),O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
89 for(int i=0; i<peak_count(); i++) {
90 write(peakfd,&(peaks_[i]),sizeof(bin64_t));
91 write(peakfd,*peak_hashes_[i],Sha1Hash::SIZE);
97 void FileTransfer::SetSize (size_t bytes) { // peaks/root must be already set
99 completek_ = complete_ = seq_complete_ = 0;
100 sizek_ = (size_>>10) + ((size_&1023) ? 1 : 0);
104 if (st.st_size!=bytes)
105 if (ftruncate(fd_, bytes))
106 return; // remain in the 0-state
107 // mmap the hash file into memory
108 std::string file_name = GetTempFilename(root_hash_,instance,std::string(".hashes"));
109 hashfd_ = open(file_name.c_str(),O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
110 size_t expected_size_ = Sha1Hash::SIZE * sizek_ * 2;
111 struct stat hash_file_st;
112 fstat(hashfd_, &hash_file_st);
113 if ( hash_file_st.st_size != expected_size_ )
114 ftruncate(hashfd_, expected_size_);
116 HANDLE hashhandle = (HANDLE)_get_osfhandle(hashfd_);
117 hashmaphandle_ = CreateFileMapping(hashhandle,
123 if (hashmaphandle_ != NULL)
125 hashes_ = (Sha1Hash*)MapViewOfFile(hashmaphandle_,
132 if (hashmaphandle_ == NULL || hashes_ == NULL)
134 hashes_ = (Sha1Hash*) mmap (NULL, expected_size_, PROT_READ|PROT_WRITE,
135 MAP_SHARED, hashfd_, 0);
136 if (hashes_==MAP_FAILED)
140 size_ = sizek_ = complete_ = completek_ = seq_complete_ = 0;
141 error_ = strerror(errno); // FIXME dprintf()
142 perror("hash tree mmap failed");
145 for(int i=0; i<peak_count_; i++)
146 hashes_[peaks_[i]] = peak_hashes_[i];
150 void FileTransfer::Submit () {
151 struct stat st; // TODO: AppendData() and streaming
154 sizek_ = (size_>>10) + ((size_&1023) ? 1 : 0);
155 hashes_ = (Sha1Hash*) malloc(Sha1Hash::SIZE*sizek_*2);
156 peak_count_ = bin64_t::peaks(sizek_,peaks_);
157 for (int p=0; p<peak_count_; p++) {
158 for(bin64_t b=peaks_[p].left_foot(); b.within(peaks_[p]); b=b.next_dfsio(0))
161 size_t rd = pread(fd_,kilo,1<<10,b.base_offset()<<10);
162 hashes_[b] = Sha1Hash(kilo,rd);
164 hashes_[b] = Sha1Hash(hashes_[b.left()],hashes_[b.right()]);
165 peak_hashes_[p] = hashes_[peaks_[p]];
166 //ack_out_.set(peaks_[p],bins::FILLED);
169 root_hash_ = DeriveRoot();
170 Sha1Hash *hash_tmp = hashes_;
173 seq_complete_ = complete_ = size_;
175 memcpy(hashes_,hash_tmp,sizek_*Sha1Hash::SIZE*2);
180 bin64_t FileTransfer::peak_for (bin64_t pos) const {
182 while (pi<peak_count_ && !pos.within(peaks_[pi]))
184 return pi==peak_count_ ? bin64_t(bin64_t::NONE) : peaks_[pi];
188 void FileTransfer::OfferHash (bin64_t pos, const Sha1Hash& hash) {
189 if (!size_) // only peak hashes are accepted at this point
190 return OfferPeak(pos,hash);
192 while (pi<peak_count_ && !pos.within(peaks_[pi]))
196 if (pos==peaks_[pi] && hash!=peak_hashes_[pi])
198 else if (ack_out_.get(pos.parent())!=bins::EMPTY)
199 return; // have this hash already, even accptd data
204 bool FileTransfer::OfferData (bin64_t pos, const uint8_t* data, size_t length) {
207 if (length<1024 && pos!=bin64_t(0,sizek_-1))
209 if (ack_out_.get(pos)==bins::FILLED)
211 bin64_t peak = peak_for(pos);
212 if (peak==bin64_t::NONE)
215 Sha1Hash hash(data,length);
217 while ( p!=peak && ack_out_.get(p)==bins::EMPTY ) {
220 hash = Sha1Hash(hashes_[p.left()],hashes_[p.right()]) ;
222 if (hash!=hashes_[p])
225 //printf("g %lli %s\n",(uint64_t)pos,hash.hex().c_str());
226 // walk to the nearest proven hash FIXME 0-layer peak
228 pwrite(fd_,data,length,pos.base_offset()<<10);
232 size_ -= 1024 - length;
233 ftruncate(fd_, size_);
235 while (ack_out_.get(bin64_t(0,seq_complete_>>10))==bins::FILLED)
237 if (seq_complete_>size_)
238 seq_complete_ = size_;
243 /*bin64_t FileTransfer::RevealAck (uint64_t& offset) {
244 if (offset<data_in_off_)
245 offset = data_in_off_;
246 for(int off=offset-data_in_off_; off<data_in_.size(); off++) {
248 if (data_in_[off]!=bin64_t::NONE) {
249 bin64_t parent = data_in_[off].parent();
250 if (ack_out_.get(parent)!=bins::FILLED)
251 return data_in_[off];
253 data_in_[off] = bin64_t::NONE;
256 return bin64_t::NONE;
260 void FileTransfer::OnDataIn (bin64_t pos) {
261 ack_out_.set(pos,bins::FILLED);
262 /*bin64_t closed = pos;
263 while (ack_out_.get(closed.parent())==bins::FILLED) // TODO optimize
264 closed = closed.parent();
265 data_in_.push_back(closed);
266 // rotating the queue
267 bin64_t parent = data_in_.front().parent();
268 if (ack_out_.get(parent)!=bins::FILLED)
269 data_in_.push_back(data_in_.front());
270 data_in_.front() = bin64_t::NONE;
271 while ( !data_in_.empty() && data_in_.front()==bin64_t::NONE) {
272 data_in_.pop_front();
278 Sha1Hash FileTransfer::DeriveRoot () {
279 int c = peak_count_-1;
280 bin64_t p = peaks_[c];
281 Sha1Hash hash = peak_hashes_[c];
283 while (p!=bin64_t::ALL) {
286 hash = Sha1Hash(hash,Sha1Hash::ZERO);
288 if (c<0 || peaks_[c]!=p.sibling())
289 return Sha1Hash::ZERO;
290 hash = Sha1Hash(peak_hashes_[c],hash);
294 //printf("p %lli %s\n",(uint64_t)p,hash.hex().c_str());
300 void FileTransfer::OfferPeak (bin64_t pos, const Sha1Hash& hash) {
303 bin64_t last_peak = peaks_[peak_count_-1];
304 if ( pos.layer()>=last_peak.layer() ||
305 pos.base_offset()!=last_peak.base_offset()+last_peak.width() )
308 peaks_[peak_count_] = pos;
309 peak_hashes_[peak_count_++] = hash;
310 // check whether peak hash candidates add up to the root hash
311 Sha1Hash mustbe_root = DeriveRoot();
312 if (mustbe_root!=root_hash_)
314 // bingo, we now know the file size (rounded up to a KByte)
315 SetSize( (pos.base_offset()+pos.width()) << 10 );
320 FileTransfer::~FileTransfer ()
323 UnmapViewOfFile(hashes_);
324 CloseHandle(hashmaphandle_);
326 munmap(hashes_,sizek_*2*Sha1Hash::SIZE);
334 FileTransfer* FileTransfer::Find (const Sha1Hash& root_hash) {
335 for(int i=0; i<files.size(); i++)
336 if (files[i] && files[i]->root_hash()==root_hash)
342 std::string FileTransfer::GetTempFilename(Sha1Hash& root_hash, int instance, std::string postfix)
344 std::string tempfile = gettmpdir();
345 std::stringstream ss;
347 tempfile += std::string(".") + root_hash.hex() + std::string(".") + ss.str() + postfix;
352 /*int p2tp::Open (const char* filename, const Sha1Hash& hash) {
353 FileTransfer* ft = new FileTransfer(filename, hash);
354 int fdes = ft->file_descriptor();
356 if (FileTransfer::files.size()<fdes)
357 FileTransfer::files.resize(fdes);
358 FileTransfer::files[fdes] = ft;
367 void FileTransfer::OnPexIn (const Address& addr) {
368 for(int i=0; i<hs_in_.size(); i++) {
369 Channel* c = Channel::channels[hs_in_[i]];
370 if (c && c->file_==this && c->peer_==addr)
371 return; // already connected
373 if (hs_in_.size()<20) {
374 new Channel(this,Channel::sockets[0],addr);
376 pex_in_.push_back(addr);
377 if (pex_in_.size()>1000)
383 int FileTransfer::RevealChannel (int& pex_out_) {
384 pex_out_ -= hs_in_offset_;
387 while (pex_out_<hs_in_.size()) {
388 Channel* c = Channel::channels[hs_in_[pex_out_]];
389 if (c && c->file_==this) {
390 pex_out_ += hs_in_offset_ + 1;
393 hs_in_[pex_out_] = hs_in_[0];
398 pex_out_ += hs_in_offset_;
404 for(int i=0; i<peak_hash_count; i++) {
405 bin64_t x = peaks[i], end = x.sibling();
407 while (!x.layer()>10) {
408 OfferHash(x.right(), hashes[x.right()]);
409 if ( ! OfferHash(x.left(), hashes[x.left()]) )
417 size_t rd = pread(fd,data,2<<10,x.base_offset());
418 if (hashes[x]==Sha1Hash(data,rd))
419 ack_out->set(x,bins::FILLED);
420 // may avoid hashing by checking whether it is zero
421 // and whether the hash matches hash of zero
423 ack_out->set(x,bins::FILLED);
427 while (x.is_right() && x!=peaks[i])
437 if ( hash_file_st.st_size < (sizeof(bin64_t)+Sha1Hash::SIZE)*64 )
442 lseek(hashfd_,0,SEEK_SET);
443 read(hashfd_,&binbuf,sizeof(bin64_t));
444 read(hashfd_,hashbuf,Sha1Hash::SIZE);
445 Sha1Hash mustberoot(false,(const char*)hashbuf);
446 if ( binbuf!=bin64_t::ALL || mustberoot != this->root_hash ) {
447 ftruncate(hashfd_,Sha1Hash::SIZE*64);
451 for(int i=1; i<64 && !this->size; i++){
452 read(hashfd_,&binbuf,sizeof(bin64_t));
453 read(hashfd_,hashbuf,Sha1Hash::SIZE);
454 Sha1Hash mustbepeak(false,(const char*)hashbuf);
455 if (mustbepeak==Sha1Hash::ZERO)
457 OfferPeak(binbuf,mustbepeak);