}
void bins::twist (uint64_t mask) {
- assert( (1<<height) > mask );
+ while ( (1<<height) <= mask )
+ extend_range();
twist_mask = mask;
}
}
}
+uint64_t bins::seq_length () {
+ iterator i(this);
+ if (!i.deep() && *i==FILLED)
+ return i.pos.width();
+ while (!i.pos.is_base()) {
+ if (i.deep() || *i!=FILLED)
+ i.left();
+ else
+ i.sibling();
+ }
+ return i.pos.base_offset() + (*i==FILLED ? 1 : 0);
+}
+
binheap::binheap() {
size_ = 32;
heap_ = (bin64_t*) malloc(size_*sizeof(bin64_t));
uint32_t size() { return cells_allocated; }
+ uint64_t seq_length ();
+
bin64_t cover(bin64_t val);
uint64_t mass ();
}\r
\r
\r
- void print_error(const char* msg) {\r
- perror(msg);\r
-#ifdef _WIN32\r
- int e = WSAGetLastError();\r
- if (e)\r
- fprintf(stderr,"network error #%i\n",e);\r
-#endif\r
- }\r
\r
\r
}; // namespace\r
#include <arpa/inet.h>
#endif
#include "datagram.h"
+#include "compat.h"
namespace p2tp {
}
tint Datagram::Time () {
- HiResTimeOfDay* tod = HiResTimeOfDay::Instance();
- tint ret = tod->getTimeUSec();
+ //HiResTimeOfDay* tod = HiResTimeOfDay::Instance();
+ //tint ret = tod->getTimeUSec();
//DLOG(INFO)<<"now is "<<ret;
- return now=ret;
+ return now = usec_time();
}
SOCKET Datagram::Bind (Address addr_) {
length+=8;
}
void PushHash (const Sha1Hash& hash) {
- Push(hash.bits, Sha1Hash::SIZE);
+ Push((uint8_t*)hash.bits, Sha1Hash::SIZE);
}
uint8_t Pull8() {
char* filename = args[2];
- Address tracker(args[3]), bindaddr(args[4]);
+ Address tracker(args[3]), bindaddr;
if (tracker==Address()) {
fprintf(stderr,"Tracker address format: [1.2.3.4:]12345\n");
return -2;
}
- if (bindaddr==Address())
- bindaddr = Address(rand()%10000+7000);
+ if (argn>=5)
+ bindaddr = Address(args[4]);
+ else
+ bindaddr = Address(INADDR_ANY,rand()%10000+7000);
assert(0<p2tp::Listen(bindaddr));
#include "hashtree.h"
#include <openssl/sha.h>
#include <string.h>
-#ifndef _WIN32
-#include <sys/mman.h>
-#endif
-#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
+#include "compat.h"
-using namespace std;
+using namespace p2tp;
#define HASHSZ 20
const size_t Sha1Hash::SIZE = HASHSZ;
const Sha1Hash Sha1Hash::ZERO = Sha1Hash();
Sha1Hash::Sha1Hash(const Sha1Hash& left, const Sha1Hash& right) {
- uint8_t data[HASHSZ*2];
+ char data[HASHSZ*2];
memcpy(data,left.bits,SIZE);
memcpy(data+SIZE,right.bits,SIZE);
- SHA1(data,SIZE*2,bits);
+ SHA1((unsigned char*)data,SIZE*2,bits);
}
-Sha1Hash::Sha1Hash(const uint8_t* data, size_t length) {
- SHA1(data,length,bits);
+Sha1Hash::Sha1Hash(const char* data, size_t length) {
+ if (length==-1)
+ length = strlen(data);
+ SHA1((unsigned char*)data,length,bits);
}
-Sha1Hash::Sha1Hash(const char* str) {
- SHA1((const unsigned char*)str,strlen(str),bits);
+Sha1Hash::Sha1Hash(const uint8_t* data, size_t length) {
+ SHA1(data,length,bits);
}
Sha1Hash::Sha1Hash(bool hex, const char* hash) {
sscanf(hx, "%x", &val);
bits[i] = val;
}
- assert(this->hex()==string(hash));
+ assert(this->hex()==std::string(hash));
} else
memcpy(bits,hash,SIZE);
}
-string Sha1Hash::hex() const {
+std::string Sha1Hash::hex() const {
char hex[HASHSZ*2+1];
for(int i=0; i<HASHSZ; i++)
- sprintf(hex+i*2, "%02x", bits[i]);
- return string(hex,HASHSZ*2);
+ sprintf(hex+i*2, "%02x", (int)(unsigned char)bits[i]);
+ return std::string(hex,HASHSZ*2);
+}
+
+
+
+/** H a s h t r e e */
+
+
+HashTree::HashTree (const char* filename, const Sha1Hash& root_hash, const char* hash_filename) :
+root_hash_(root_hash), fd_(0), hash_fd_(0), data_recheck_(false),
+peak_count_(0), hashes_(NULL), size_(0), sizek_(0),
+complete_(0), completek_(0)
+{
+ fd_ = open(filename,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ if (fd_<0)
+ return;
+ char hfn[1024] = "";
+ if (!hash_filename) {
+ strcat(hfn, filename);
+ strcat(hfn, ".mhash");
+ } else
+ strcpy(hfn,hash_filename);
+ hash_fd_ = open(hfn,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ if (hash_fd_<0)
+ return;
+ if (root_hash_==Sha1Hash::ZERO) { // fresh submit, hash it
+ assert(file_size(fd_));
+ Submit();
+ } else {
+ RecoverProgress();
+ } // else LoadComplete()
+}
+
+
+void HashTree::Submit () {
+ size_ = file_size(fd_);
+ sizek_ = (size_>>10) + ((size_&1023) ? 1 : 0);
+ peak_count_ = bin64_t::peaks(sizek_,peaks_);
+ int hashes_size = Sha1Hash::SIZE*sizek_*2;
+ file_resize(hash_fd_,hashes_size);
+ hashes_ = (Sha1Hash*) memory_map(hash_fd_,hashes_size);
+ if (!hashes_) {
+ size_ = sizek_ = complete_ = completek_ = 0;
+ print_error("mmap failed");
+ return;
+ }
+ for (size_t i=0; i<sizek_; i++) {
+ char kilo[1<<10];
+ size_t rd = read(fd_,kilo,1<<10);
+ if (rd<(1<<10) && i!=sizek_-1) {
+ free(hashes_);
+ hashes_=NULL;
+ return;
+ }
+ bin64_t pos(0,i);
+ hashes_[pos] = Sha1Hash(kilo,rd);
+ ack_out_.set(pos);
+ complete_+=rd;
+ completek_++;
+ }
+ for (int p=0; p<peak_count_; p++) {
+ if (!peaks_[p].is_base())
+ for(bin64_t b=peaks_[p].left_foot().parent(); b.within(peaks_[p]); b=b.next_dfsio(1))
+ hashes_[b] = Sha1Hash(hashes_[b.left()],hashes_[b.right()]);
+ peak_hashes_[p] = hashes_[peaks_[p]];
+ }
+
+ root_hash_ = DeriveRoot();
+
}
+/** Basically, simulated receiving every single packet, except
+ for some optimizations. */
+void HashTree::RecoverProgress () {
+ size_t size = file_size(fd_);
+ size_t sizek = (size>>10) + ((size&1023) ? 1 : 0);
+ bin64_t peaks[64];
+ int peak_count = bin64_t::peaks(sizek,peaks);
+ for(int i=0; i<peak_count; i++) {
+ Sha1Hash peak_hash;
+ file_seek(hash_fd_,peaks[i]*sizeof(Sha1Hash));
+ if (read(hash_fd_,&peak_hash,sizeof(Sha1Hash))!=sizeof(Sha1Hash))
+ return;
+ OfferPeakHash(peaks[i], peak_hash);
+ }
+ if (!this->size())
+ return; // if no valid peak hashes found
+ // at this point, we may use mmapd hashes already
+ // so, lets verify hashes and the data we've got
+ char zeros[1<<10];
+ memset(zeros, 0, 1<<10);
+ Sha1Hash kilo_zero(zeros,1<<10);
+ for(int p=0; p<size_kilo(); p++) {
+ char buf[1<<10];
+ bin64_t pos(0,p);
+ if (hashes_[pos]==Sha1Hash::ZERO)
+ continue;
+ size_t rd = read(fd_,buf,1<<10);
+ assert(rd==(1<<10) || p==size_kilo()-1);
+ if (rd==(1<<10) && !memcmp(buf, zeros, rd) && hashes_[pos]!=kilo_zero)
+ continue;
+ if ( data_recheck_ && !OfferHash(pos, Sha1Hash(buf,rd)) )
+ continue;
+ ack_out_.set(pos);
+ completek_++;
+ complete_+=rd;
+ }
+}
+
-/*void HashTree::expand (bin tolen) {
- if (bits)
- munmap(bits,length*HASHSZ);
- length = tolen;
- status.resize(length);
- bits = (Sha1Hash*) mmap(NULL,length*HASHSZ,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
-}*/
+bool HashTree::OfferPeakHash (bin64_t pos, const Sha1Hash& hash) {
+ assert(!size_);
+ if (peak_count_) {
+ bin64_t last_peak = peaks_[peak_count_-1];
+ if ( pos.layer()>=last_peak.layer() ||
+ pos.base_offset()!=last_peak.base_offset()+last_peak.width() )
+ peak_count_ = 0;
+ }
+ peaks_[peak_count_] = pos;
+ peak_hashes_[peak_count_] = hash;
+ peak_count_++;
+ // check whether peak hash candidates add up to the root hash
+ Sha1Hash mustbe_root = DeriveRoot();
+ if (mustbe_root!=root_hash_)
+ return false;
+ for(int i=0; i<peak_count_; i++)
+ sizek_ += peaks_[i].width();
+
+ // bingo, we now know the file size (rounded up to a KByte)
+
+ size_ = sizek_<<10;
+ completek_ = complete_ = 0;
+ sizek_ = (size_>>10) + ((size_&1023) ? 1 : 0);
+
+ // FIXME check tail here
+ if (file_size(fd_)!=size_)
+ if (file_resize(fd_, size_)) {
+ print_error("cannot set file size\n");
+ size_=0; // remain in the 0-state
+ return false;
+ }
+
+ // mmap the hash file into memory
+ size_t expected_size = sizeof(Sha1Hash)*sizek_*2;
+ if ( file_size(hash_fd_) != expected_size )
+ file_resize (hash_fd_, expected_size);
+
+ hashes_ = (Sha1Hash*) memory_map(hash_fd_,expected_size);
+ if (!hashes_) {
+ size_ = sizek_ = complete_ = completek_ = 0;
+ print_error("mmap failed");
+ return false;
+ }
+
+ for(int i=0; i<peak_count_; i++)
+ hashes_[peaks_[i]] = peak_hashes_[i];
+ return true;
+}
-/*Sha1Hash HashTree::deriveRoot () {
- int i = peaks.size()-1;
- bin p = peaks[i].first;
- Sha1Hash hash = peaks[i].second;
- i--;
- while (p<bin::ALL) {
+Sha1Hash HashTree::DeriveRoot () {
+ int c = peak_count_-1;
+ bin64_t p = peaks_[c];
+ Sha1Hash hash = peak_hashes_[c];
+ c--;
+ while (p!=bin64_t::ALL) {
if (p.is_left()) {
p = p.parent();
hash = Sha1Hash(hash,Sha1Hash::ZERO);
} else {
- if (i<0 || peaks[i].first!=p.sibling())
+ if (c<0 || peaks_[c]!=p.sibling())
return Sha1Hash::ZERO;
- hash = Sha1Hash(peaks[i].second,hash);
+ hash = Sha1Hash(peak_hashes_[c],hash);
p = p.parent();
- i--;
+ c--;
}
+ //dprintf("p %lli %s\n",(uint64_t)p,hash.hex().c_str());
}
- return hash;
+ return hash;
}
-HashTree::HashTree (int fd) {
- //fd = open(filename,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
- if (fd<0) return;
- struct stat st;
- fstat(fd, &st);
- length = (st.st_size>>10) + (st.st_size%1024 ? 1 : 0);
- mass = bin::lenpeak(length); // incorrect
- bits.resize(mass+1);
- status.resize(mass+1);
- uint8_t buf[1024];
- for(bin i=1; i<=mass; i++)
- if (i.layer()) {
- bits[i] = Sha1Hash(bits[i.left()],bits[i.right()]);
- } else {
- int len = pread(fd,buf,1024,i.offset()<<10);
- bits[i] = Sha1Hash(buf,len);
- }
- //close(fd);
- bin::vec p = bin::peaks(length);
- while(p.size()) {
- peaks.push_back(binhash(p.front(),bits[p.front()]));
- p.pop_front();
- }
- root = deriveRoot();
+
+/** For live streaming: appends the data, adjusts the tree.
+ @ return the number of fresh (tail) peak hashes */
+int HashTree::AppendData (char* data, int length) {
+ return 0;
}
-HashTree::HashTree (const Sha1Hash& with_root) : root(with_root), length(0), mass(0) {
- // recover the partially filled hash file
- // first, size
- // then, peaks
- // works? then offer the rest
+
+bin64_t HashTree::peak_for (bin64_t pos) const {
+ int pi=0;
+ while (pi<peak_count_ && !pos.within(peaks_[pi]))
+ pi++;
+ return pi==peak_count_ ? bin64_t(bin64_t::NONE) : peaks_[pi];
}
-HashTree::~HashTree () {
- close(fd);
+
+bool HashTree::OfferHash (bin64_t pos, const Sha1Hash& hash) {
+ if (!size_) // only peak hashes are accepted at this point
+ return OfferPeakHash(pos,hash);
+ bin64_t peak = peak_for(pos);
+ if (peak==bin64_t::NONE)
+ return false;
+ if (peak==pos)
+ return hash == hashes_[pos];
+ if (ack_out_.get(pos.parent())!=bins::EMPTY)
+ return hash==hashes_[pos]; // have this hash already, even accptd data
+ hashes_[pos] = hash;
+ if (!pos.is_base())
+ return false; // who cares?
+ bin64_t p = pos;
+ Sha1Hash uphash = hash;
+ while ( p!=peak && ack_out_.get(p)==bins::EMPTY ) {
+ hashes_[p] = uphash;
+ p = p.parent();
+ uphash = Sha1Hash(hashes_[p.left()],hashes_[p.right()]) ;
+ }// walk to the nearest proven hash
+ return uphash==hashes_[p];
}
-HashTree::hashres_t HashTree::offerPeak (bin pos, Sha1Hash hash) {
- if (bin(pos+1).layer())
- return REJECT;
- if (bin::all1(pos))
- peaks.clear();
- peaks.push_back(binhash(pos,hash));
- if (deriveRoot()==root) { // bingo
- mass = peaks.back().first;
- length = mass.length();
- status.resize(mass+1);
- bits.resize(mass+1);
- for(int i=0; i<peaks.size(); i++) {
- bits[peaks[i].first] = peaks[i].second;
- status[peaks[i].first] = true;
- }
- return PEAK_ACCEPT;
- } else
- return pos.layer() ? DUNNO : REJECT;
+
+bool HashTree::OfferData (bin64_t pos, const char* data, size_t length) {
+ if (!pos.is_base())
+ return false;
+ if (length<1024 && pos!=bin64_t(0,sizek_-1))
+ return false;
+ if (ack_out_.get(pos)==bins::FILLED)
+ return true; // to set data_in_
+ bin64_t peak = peak_for(pos);
+ if (peak==bin64_t::NONE)
+ return false;
+
+ if (!OfferHash(pos, Sha1Hash(data,length)))
+ return false;
+
+ //printf("g %lli %s\n",(uint64_t)pos,hash.hex().c_str());
+ ack_out_.set(pos,bins::FILLED);
+ pwrite(fd_,data,length,pos.base_offset()<<10);
+ complete_ += length;
+ completek_++;
+ if (length<1024 && !(size_&1023)) {
+ size_ -= 1024 - length;
+ file_resize(fd_, size_);
+ }
+ return true;
}
-HashTree::hashres_t HashTree::offer (bin pos, const Sha1Hash& hash) {
- if (!length) // only peak hashes are accepted at this point
- return offerPeak(pos,hash);
- if (pos>mass)
- return REJECT;
- if (status[pos])
- return bits[pos]==hash ? ACCEPT : REJECT;
- bits[pos] = hash;
- // walk to the nearest proven hash
- if (bits[pos.sibling()]==Sha1Hash::ZERO)
- return DUNNO;
- bin p = pos.parent();
- while (!status[p]) {
- bits[p] = Sha1Hash(bits[p.left()],bits[p.right()]);
- p = p.parent();
- }
- if ( bits[p] == Sha1Hash(bits[p.left()],bits[p.right()]) ) {
- for(bin i=pos; i<p; i=i.parent())
- status[i] = status[i.sibling()] = true;
- return ACCEPT;
- } else
- return REJECT;
+uint64_t HashTree::seq_complete () {
+ uint64_t seqk = ack_out_.seq_length();
+ if (seqk==sizek_)
+ return size_;
+ else
+ return seqk<<10;
}
-*/
+HashTree::~HashTree () {
+ if (hashes_)
+ memory_unmap(hash_fd_, hashes_, sizek_*2*sizeof(Sha1Hash));
+ if (fd_)
+ close(fd_);
+}
\ No newline at end of file
*/
#ifndef P2TP_SHA1_HASH_TREE_H
#define P2TP_SHA1_HASH_TREE_H
-#include "bin.h"
+#include "bin64.h"
+#include "bins.h"
#include <string.h>
#include <string>
-#include <vector>
+
+namespace p2tp {
+
struct Sha1Hash {
uint8_t bits[20];
Sha1Hash() { memset(bits,0,20); }
Sha1Hash(const Sha1Hash& left, const Sha1Hash& right);
- Sha1Hash(const uint8_t* bits, size_t length);
- /***/
- Sha1Hash(const char* bits);
+ Sha1Hash(const char* str, size_t length=-1);
+ Sha1Hash(const uint8_t* data, size_t length);
Sha1Hash(bool hex, const char* hash);
std::string hex() const;
const static size_t SIZE;
};
-/*
-typedef std::pair<bin,Sha1Hash> binhash;
-struct HashTree {
- Sha1Hash root;
- int fd;
- bin mass;
- uint32_t length;
- std::vector<bool> status;
- std::vector<Sha1Hash> bits;
- std::vector<binhash> peaks;
- typedef enum { ACCEPT, DUNNO, PEAK_ACCEPT, REJECT } hashres_t;
+class HashTree {
+
+ /** Merkle hash tree: root */
+ Sha1Hash root_hash_;
+ Sha1Hash *hashes_;
+ /** Merkle hash tree: peak hashes */
+ Sha1Hash peak_hashes_[64];
+ bin64_t peaks_[64];
+ int peak_count_;
+ /** File descriptor to put hashes to */
+ int fd_;
+ int hash_fd_;
+ /** Whether to re-hash files. */
+ bool data_recheck_;
+ /** Base size, as derived from the hashes. */
+ size_t size_;
+ size_t sizek_;
+ /** Part of the tree currently checked. */
+ size_t complete_;
+ size_t completek_;
+ bins ack_out_;
+
protected:
- Sha1Hash deriveRoot();
- hashres_t offerPeak (bin pos, Sha1Hash hash);
+
+ void Submit();
+ void RecoverProgress();
+ Sha1Hash DeriveRoot();
+ bool OfferPeakHash (bin64_t pos, const Sha1Hash& hash);
+
public:
- HashTree (int fd);
- HashTree (const Sha1Hash& root);
+ HashTree (const char* file_name, const Sha1Hash& root=Sha1Hash::ZERO,
+ const char* hash_filename=NULL);
+
+ /** Offer a hash; returns true if it verified; false otherwise.
+ Once it cannot be verified (no sibling or parent), the hash
+ is remembered, while returning false. */
+ bool OfferHash (bin64_t pos, const Sha1Hash& hash);
+ /** Offer data; the behavior is the same as with a hash:
+ accept or remember or drop. Returns true => ACK is sent. */
+ bool OfferData (bin64_t bin, const char* data, size_t length);
+ /** Not implemented yet. */
+ int AppendData (char* data, int length) ;
+
+ int file_descriptor () const { return fd_; }
+ int peak_count () const { return peak_count_; }
+ bin64_t peak (int i) const { return peaks_[i]; }
+ const Sha1Hash& peak_hash (int i) const { return peak_hashes_[i]; }
+ bin64_t peak_for (bin64_t pos) const;
+ const Sha1Hash& hash (bin64_t pos) const {return hashes_[pos];}
+ const Sha1Hash& root_hash () const { return root_hash_; }
+ uint64_t size () const { return size_; }
+ uint64_t size_kilo () const { return sizek_; }
+ uint64_t complete () const { return complete_; }
+ uint64_t complete_kilo () const { return completek_; }
+ uint64_t seq_complete () ;
+ bool is_complete ()
+ { return size_ && seq_complete()==size_; }
+ bins& ack_out () { return ack_out_; }
~HashTree ();
- hashres_t offer (bin pos, const Sha1Hash& hash);
-
- bool rooted () const { return length>0; }
-
- const Sha1Hash& operator [] (bin i) {
- return i<=mass ? bits[i] : Sha1Hash::ZERO;
- }
- uint32_t data_size () const { return length; }
-
- bin data_mass () const { return mass; }
-
- const std::vector<binhash>& peak_hashes() const { return peaks; }
-
-};*/
+};
+
+}
#endif
#include "ext/simple_selector.cpp"
PeerSelector* Channel::peer_selector = new SimpleSelector();
-Channel::Channel (FileTransfer* file, int socket, Address peer_addr) :
- file_(file), peer_(peer_addr), peer_channel_id_(0), pex_out_(0),
+Channel::Channel (FileTransfer* transfer, int socket, Address peer_addr) :
+ transfer_(transfer), peer_(peer_addr), peer_channel_id_(0), pex_out_(0),
socket_(socket==-1?sockets[0]:socket), // FIXME
own_id_mentioned_(false), next_send_time_(0), last_send_time_(0),
last_recv_time_(0), rtt_avg_(TINT_SEC), dev_avg_(0), dip_avg_(TINT_SEC)
int p2tp::Open (const char* filename, const Sha1Hash& hash) {
FileTransfer* ft = new FileTransfer(filename, hash);
- int fdes = ft->file_descriptor();
+ int fdes = ft->file().file_descriptor();
if (fdes>0) {
/*if (FileTransfer::files.size()<fdes) // FIXME duplication
size_t p2tp::Size (int fdes) {
if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
- return FileTransfer::files[fdes]->size();
+ return FileTransfer::files[fdes]->file().size();
else
return 0;
}
size_t p2tp::Complete (int fdes) {
if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
- return FileTransfer::files[fdes]->is_complete();
+ return FileTransfer::files[fdes]->file().is_complete();
else
return 0;
}
size_t p2tp::SeqComplete (int fdes) {
if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
- return FileTransfer::files[fdes]->seq_complete();
+ return FileTransfer::files[fdes]->file().seq_complete();
else
return 0;
}
FileTransfer* trans = FileTransfer::file(file);
if (!trans)
return Sha1Hash::ZERO;
- return trans->root_hash();
+ return trans->file().root_hash();
}
/** Close everything. */
~FileTransfer();
- /** Offer a hash; returns true if it verified; false otherwise.
- Once it cannot be verified (no sibling or parent), the hash
- is remembered, while returning false. */
- void OfferHash (bin64_t pos, const Sha1Hash& hash);
- /** Offer data; the behavior is the same as with a hash:
- accept or remember or drop. Returns true => ACK is sent. */
- bool OfferData (bin64_t bin, const uint8_t* data, size_t length);
/** While we need to feed ACKs to every peer, we try (1) avoid
unnecessary duplication and (2) keep minimum state. Thus,
return fd<files.size() ? files[fd] : NULL;
}
- int peak_count () const { return peak_count_; }
- bin64_t peak (int i) const { return peaks_[i]; }
- const Sha1Hash& peak_hash (int i) const { return peak_hashes_[i]; }
- bin64_t peak_for (bin64_t pos) const;
- const Sha1Hash& hash (bin64_t pos) const {
- assert(pos<sizek_*2);
- return hashes_[pos];
- }
- const Sha1Hash& root_hash () const { return root_hash_; }
- uint64_t size () const { return size_; }
- uint64_t size_kilo () const { return sizek_; }
- uint64_t complete () const { return complete_; }
- uint64_t complete_kilo () const { return completek_; }
- uint64_t seq_complete () const { return seq_complete_; }
- bool is_complete () const
- { return size_ && seq_complete_==size_; }
- bins& ack_out () { return ack_out_; }
- int file_descriptor () const { return fd_; }
+ bins& ack_out () { return file_.ack_out(); }
PiecePicker& picker () { return *picker_; }
int channel_count () const { return hs_in_.size(); }
+ HashTree& file() { return file_; }
+ int fd () const { return file_.file_descriptor(); }
+ const Sha1Hash& root_hash () const { return file_.root_hash(); }
static int instance; // FIXME this smells
private:
static std::vector<FileTransfer*> files;
- static std::string GetTempFilename(Sha1Hash& root_hash, int instance, std::string postfix);
-
- /** file descriptor. */
- int fd_;
- /** File size, as derived from the hashes. */
- size_t size_;
- size_t sizek_;
- /** Part the file currently downloaded. */
- size_t complete_;
- size_t completek_;
- size_t seq_complete_;
- /** A binmap for all packets obtained and succesfully checked. */
- bins ack_out_;
- /** History of bin retrieval. */
- //binqueue data_in_;
- //int data_in_off_;
+
+ HashTree file_;
+
/** Piece picker strategy. */
PiecePicker* picker_;
- /** File for keeping the Merkle hash tree. */
- int hashfd_;
-#ifdef _WIN32
- HANDLE hashmaphandle_;
-#endif
- /** Merkle hash tree: root */
- Sha1Hash root_hash_;
- /** Merkle hash tree: peak hashes */
- Sha1Hash peak_hashes_[64];
- bin64_t peaks_[64];
- int peak_count_;
- /** Merkle hash tree: the tree, as a bin64_t-indexed array */
- Sha1Hash* hashes_;
- /** for recovering saved state */
- bool dry_run_;
- /** Error encountered */
- char* error_;
-
+
/** Channels working for this transfer. */
binqueue hs_in_;
int hs_in_offset_;
std::deque<Address> pex_in_;
+
/** Messages we are accepting. */
uint64_t cap_out_;
protected:
- void SetSize(size_t bytes);
- void Submit();
- void RecoverProgress();
- void OfferPeak (bin64_t pos, const Sha1Hash& hash);
- Sha1Hash DeriveRoot();
- void SavePeaks();
- void LoadPeaks();
void OnDataIn (bin64_t pos);
void OnPexIn (const Address& addr);
const std::string id_string () const;
/** A channel is "established" if had already sent and received packets. */
bool is_established () { return peer_channel_id_ && own_id_mentioned_; }
- FileTransfer& file() { return *file_; }
+ FileTransfer& transfer() { return *transfer_; }
+ HashTree& file () { return transfer_->file(); }
const Address& peer() const { return peer_; }
static int DecodeID(int scrambled);
/** The UDP socket fd. */
int socket_;
/** Descriptor of the file in question. */
- FileTransfer* file_;
+ FileTransfer* transfer_;
/** Peer channel id; zero if we are trying to open a channel. */
uint32_t peer_channel_id_;
bool own_id_mentioned_;
if ( 4*peer_cwnd > hinted ) { //hinted*1024 < peer_cwnd*4 ) {
uint8_t layer = 2; // actually, enough
- bin64_t hint = file().picker().Pick(ack_in_,layer);
+ bin64_t hint = transfer().picker().Pick(ack_in_,layer);
// FIXME FIXME FIXME: any layer
if (hint==bin64_t::NONE)
- hint = file().picker().Pick(ack_in_,0);
+ hint = transfer().picker().Pick(ack_in_,0);
if (hint!=bin64_t::NONE) {
hint_out_.push_back(hint);
rtt_avg_ = NOW - last_send_time_;
dev_avg_ = rtt_avg_;
dip_avg_ = rtt_avg_;
- file_->hs_in_.push_back(id);
+ transfer().hs_in_.push_back(id);
dprintf("%s #%i rtt init %lli\n",tintstr(),id,rtt_avg_);
}
bin64_t data = dgram.size() ? bin64_t::NONE : bin64_t::ALL;
bin64_t pos = dgram.Pull32();
uint8_t *data;
int length = dgram.Pull(&data,1024);
- bool ok = file().OfferData(pos, data, length) ;
+ bool ok = file().OfferData(pos, (char*)data, length) ;
dprintf("%s #%i %cdata (%lli)\n",tintstr(),id,ok?'-':'!',pos.offset());
if (ok) {
data_in_ = tintbin(NOW,pos);
uint16_t port = dgram.Pull16();
Address addr(ipv4,port);
dprintf("%s #%i -pex %s\n",tintstr(),id,addr.str().c_str());
- file_->OnPexIn(addr);
+ transfer().OnPexIn(addr);
}
void Channel::AddPex (Datagram& dgram) {
- int chid = file_->RevealChannel(pex_out_);
+ int chid = transfer().RevealChannel(pex_out_);
if (chid==-1 || chid==id)
return;
Address a = channels[chid]->peer();
uint8_t hashid = data.Pull8();
if (hashid!=P2TP_HASH)
RETLOG ("no hash in the initial handshake");
- bin pos = data.Pull32();
+ bin64_t pos = data.Pull32();
if (pos!=bin64_t::ALL32)
RETLOG ("that is not the root hash");
hash = data.PullHash();
EXPECT_EQ(bins::EMPTY,b.get(bin64_t(3,3)));
}
+TEST(BinsTest,SeqLength) {
+ bins b;
+ b.set(bin64_t(3,0));
+ b.set(bin64_t(1,4));
+ b.set(bin64_t(0,10));
+ b.set(bin64_t(3,2));
+ EXPECT_EQ(11,b.seq_length());
+}
+
TEST(BinheapTest,Eat) {
binheap b;
*
*/
#include <fcntl.h>
-#include "bin.h"
+#include "bin64.h"
#include <gtest/gtest.h>
#include "hashtree.h"
+using namespace p2tp;
+
char hash123[] = "a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0";
-//char roothash123[] = "84e1e5f4b549fe072d803709eeb06143cd2ad736";
+char rooth123[] = "d0bdb8ba28076d84d2b3a0e62521b998e42349a1";
TEST(Sha1HashTest,Trivial) {
Sha1Hash hash("123\n");
EXPECT_STREQ(hash123,hash.hex().c_str());
}
-/*
-TEST(Sha1HashTest,HashTreeTest) {
- Sha1Hash roothash123(hash123);
- for(bin pos=1; pos<bin::ALL; pos=pos.parent())
+
+TEST(Sha1HashTest,OfferDataTest) {
+ Sha1Hash roothash123(true,hash123);
+ for(bin64_t pos(0,0); pos!=bin64_t::ALL; pos=pos.parent())
roothash123 = Sha1Hash(roothash123,Sha1Hash::ZERO);
- HashTree tree = HashTree(roothash123);
- ASSERT_EQ(HashTree::PEAK_ACCEPT, tree.offer(bin(1),hash123));
- ASSERT_TRUE(tree.rooted());
+ unlink("123");
+ EXPECT_STREQ(rooth123,roothash123.hex().c_str());
+ HashTree tree("123",roothash123);
+ tree.OfferHash(bin64_t(0,0),Sha1Hash(true,hash123));
+ ASSERT_EQ(1,tree.size_kilo());
+ ASSERT_TRUE(tree.OfferData(bin64_t(0,0), "123\n", 4));
+ unlink("123");
+ ASSERT_EQ(4,tree.size());
+}
+
+
+TEST(Sha1HashTest,SubmitTest) {
+ FILE* f123 = fopen("123","w+");
+ fprintf(f123, "123\n");
+ fclose(f123);
+ HashTree ht123("123");
+ EXPECT_STREQ(hash123,ht123.hash(bin64_t(0,0)).hex().c_str());
+ EXPECT_STREQ(rooth123,ht123.root_hash().hex().c_str());
+ EXPECT_EQ(4,ht123.size());
}
-TEST(Sha1HashTest,HashFileTest) {
+
+/*TEST(Sha1HashTest,HashFileTest) {
uint8_t a [1024], b[1024], c[1024];
memset(a,'a',1024);
memset(b,'b',1024);
EXPECT_TRUE ( bootstree.bits[1]==aaahash );
EXPECT_TRUE ( bootstree.bits[2]==bbbhash );
EXPECT_FALSE ( bootstree.bits[2]==aaahash );
-}
-*/
+}*/
+
+
int main (int argc, char** argv) {
//bin::init();
//#include <gtest/gtest.h>
//#include <glog/logging.h>
#include "p2tp.h"
-#include "compat/util.h"
-#ifdef _MSC_VER
-#include "compat/unixio.h"
-#endif
+#include "compat.h"
using namespace p2tp;
-const char* BTF = "big_test_file";
+const char* BTF = "test_file";
Sha1Hash A,B,C,D,E,AB,CD,ABCD,E0,E000,ABCDE000,ROOT;
ROOT = Sha1Hash(ROOT,Sha1Hash::ZERO);
//printf("m %lli %s\n",(uint64_t)pos.parent(),ROOT.hex().c_str());
}
-
- // submit a new file
- FileTransfer* seed = new FileTransfer(BTF);
+
+ // now, submit a new file
+
+ FileTransfer* seed_transfer = new FileTransfer(BTF);
+ HashTree* seed = & seed_transfer->file();
EXPECT_TRUE(A==seed->hash(0));
EXPECT_TRUE(E==seed->hash(bin64_t(0,4)));
EXPECT_TRUE(ABCD==seed->hash(bin64_t(2,0)));
// retrieve it
unlink("copy");
FileTransfer::instance = 1;
- FileTransfer* leech = new FileTransfer("copy",seed->root_hash());
- leech->picker().Randomize(0);
+ FileTransfer* leech_transfer = new FileTransfer("copy",seed->root_hash());
+ HashTree* leech = & leech_transfer->file();
+ leech_transfer->picker().Randomize(0);
// transfer peak hashes
for(int i=0; i<seed->peak_count(); i++)
leech->OfferHash(seed->peak(i),seed->peak_hash(i));
leech->OfferHash(bin64_t(1,1), seed->hash(bin64_t(1,1)));
for (int i=0; i<5; i++) {
if (i==2) { // now: stop, save, start
- delete leech;
+ delete leech_transfer;
FileTransfer::instance = 1;
- leech = new FileTransfer("copy",seed->root_hash());
- leech->picker().Randomize(0);
+ leech_transfer = new FileTransfer("copy",seed->root_hash());
+ leech = & leech_transfer->file();
+ leech_transfer->picker().Randomize(0);
EXPECT_EQ(2,leech->complete_kilo());
}
- bin64_t next = leech->picker().Pick(seed->ack_out(),0);
+ bin64_t next = leech_transfer->picker().Pick(seed->ack_out(),0);
ASSERT_NE(bin64_t::NONE,next);
ASSERT_TRUE(next.base_offset()<5);
uint8_t buf[1024]; //size_t len = seed->storer->ReadData(next,&buf);
leech->OfferHash(sibling, seed->hash(sibling));
uint8_t memo = *buf;
*buf = 'z';
- EXPECT_FALSE(leech->OfferData(next, buf, len));
+ EXPECT_FALSE(leech->OfferData(next, (char*)buf, len));
*buf = memo;
- EXPECT_TRUE(leech->OfferData(next, buf, len));
+ EXPECT_TRUE(leech->OfferData(next, (char*)buf, len));
}
EXPECT_EQ(4100,leech->size());
EXPECT_EQ(5,leech->size_kilo());
int main (int argc, char** argv) {
- std::string tempdir = gettmpdir();
- unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.0.hashes")).c_str());
- unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.0.peaks")).c_str());
- unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.1.hashes")).c_str());
- unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.1.peaks")).c_str());
- unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.2.hashes")).c_str());
- unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.2.peaks")).c_str());
-
- unlink(BTF);
+ unlink("test_file");
unlink("copy");
+ unlink("test_file.mhash");
+ unlink("copy.mhash");
int f = open(BTF,O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (f < 0)
// FIXME: separate Bootstrap() and Download(), then Size(), Progress(), SeqProgress()
FileTransfer::FileTransfer (const char* filename, const Sha1Hash& _root_hash) :
- root_hash_(_root_hash), fd_(0), hashfd_(0), dry_run_(false),
- peak_count_(0), hashes_(NULL), error_(NULL), size_(0), sizek_(0),
- complete_(0), completek_(0), seq_complete_(0), hs_in_offset_(0)
+ file_(filename,_root_hash), hs_in_offset_(0)
{
- fd_ = open(filename,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
- if (fd_<0)
- return;
- if (files.size()<fd_+1)
- files.resize(fd_+1);
- files[fd_] = this;
- if (root_hash_==Sha1Hash::ZERO) // fresh submit, hash it
- Submit();
- else
- RecoverProgress();
+ if (files.size()<fd()+1)
+ files.resize(fd()+1);
+ files[fd()] = this;
picker_ = new SeqPiecePicker(this);
+ if (file_.size())
+ picker_->Randomize(rand()&31&(file_.peak(0).width()-1));
}
-void FileTransfer::LoadPeaks () {
- std::string file_name = GetTempFilename(root_hash_,instance,std::string(".peaks"));
- int peakfd = open(file_name.c_str(),O_RDONLY,0);
- if (peakfd<0)
- return;
- bin64_t peak;
- char hash[128];
- while (sizeof(bin64_t)==read(peakfd,&peak,sizeof(bin64_t))) {
- read(peakfd,hash,Sha1Hash::SIZE);
- OfferPeak(peak, Sha1Hash(false,hash));
- }
- close(peakfd);
-}
-
-
-/** Basically, simulated receiving every single packet, except
- for some optimizations. */
-void FileTransfer::RecoverProgress () {
- dry_run_ = true;
- LoadPeaks();
- if (!size())
- return;
- // at this point, we may use mmapd hashes already
- // so, lets verify hashes and the data we've got
- lseek(fd_,0,SEEK_SET);
- for(int p=0; p<size_kilo(); p++) {
- uint8_t buf[1<<10];
- size_t rd = read(fd_,buf,1<<10);
- OfferData(bin64_t(0,p), buf, rd);
- if (rd<(1<<10))
- break;
- }
- dry_run_ = false;
-}
-
-
-void FileTransfer::SavePeaks () {
- std::string file_name = GetTempFilename(root_hash_,instance,std::string(".peaks"));
- int peakfd = open(file_name.c_str(),O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
- for(int i=0; i<peak_count(); i++) {
- write(peakfd,&(peaks_[i]),sizeof(bin64_t));
- write(peakfd,*peak_hashes_[i],Sha1Hash::SIZE);
- }
- close(peakfd);
-}
-
-
-void FileTransfer::SetSize (size_t bytes) { // peaks/root must be already set
- size_ = bytes;
- completek_ = complete_ = seq_complete_ = 0;
- sizek_ = (size_>>10) + ((size_&1023) ? 1 : 0);
-
- struct stat st;
- fstat(fd_, &st);
- if (st.st_size!=bytes)
- if (ftruncate(fd_, bytes))
- return; // remain in the 0-state
- // mmap the hash file into memory
- std::string file_name = GetTempFilename(root_hash_,instance,std::string(".hashes"));
- hashfd_ = open(file_name.c_str(),O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
- size_t expected_size_ = Sha1Hash::SIZE * sizek_ * 2;
- struct stat hash_file_st;
- fstat(hashfd_, &hash_file_st);
- if ( hash_file_st.st_size != expected_size_ )
- ftruncate(hashfd_, expected_size_);
-#ifdef _WIN32
- HANDLE hashhandle = (HANDLE)_get_osfhandle(hashfd_);
- hashmaphandle_ = CreateFileMapping(hashhandle,
- NULL,
- PAGE_READWRITE,
- 0,
- 0,
- NULL);
- if (hashmaphandle_ != NULL)
- {
- hashes_ = (Sha1Hash*)MapViewOfFile(hashmaphandle_,
- FILE_MAP_WRITE,
- 0,
- 0,
- 0);
-
- }
- if (hashmaphandle_ == NULL || hashes_ == NULL)
-#else
- hashes_ = (Sha1Hash*) mmap (NULL, expected_size_, PROT_READ|PROT_WRITE,
- MAP_SHARED, hashfd_, 0);
- if (hashes_==MAP_FAILED)
-#endif
- {
- hashes_ = NULL;
- size_ = sizek_ = complete_ = completek_ = seq_complete_ = 0;
- error_ = strerror(errno); // FIXME dprintf()
- perror("hash tree mmap failed");
- return;
- }
- for(int i=0; i<peak_count_; i++)
- hashes_[peaks_[i]] = peak_hashes_[i];
- picker_->Randomize(rand()&31&(sizek_-1));
-}
-
-
-void FileTransfer::Submit () {
- struct stat st; // TODO: AppendData() and streaming
- fstat(fd_, &st);
- size_ = st.st_size;
- sizek_ = (size_>>10) + ((size_&1023) ? 1 : 0);
- hashes_ = (Sha1Hash*) malloc(Sha1Hash::SIZE*sizek_*2);
- peak_count_ = bin64_t::peaks(sizek_,peaks_);
- for (int p=0; p<peak_count_; p++) {
- for(bin64_t b=peaks_[p].left_foot(); b.within(peaks_[p]); b=b.next_dfsio(0))
- if (b.is_base()) {
- uint8_t kilo[1<<10];
- size_t rd = pread(fd_,kilo,1<<10,b.base_offset()<<10);
- hashes_[b] = Sha1Hash(kilo,rd);
- } else
- hashes_[b] = Sha1Hash(hashes_[b.left()],hashes_[b.right()]);
- peak_hashes_[p] = hashes_[peaks_[p]];
- //ack_out_.set(peaks_[p],bins::FILLED);
- OnDataIn(peaks_[p]);
- }
- root_hash_ = DeriveRoot();
- Sha1Hash *hash_tmp = hashes_;
- SetSize(st.st_size);
- SavePeaks();
- seq_complete_ = complete_ = size_;
- completek_ = sizek_;
- memcpy(hashes_,hash_tmp,sizek_*Sha1Hash::SIZE*2);
- free(hash_tmp);
-}
-
-
-bin64_t FileTransfer::peak_for (bin64_t pos) const {
- int pi=0;
- while (pi<peak_count_ && !pos.within(peaks_[pi]))
- pi++;
- return pi==peak_count_ ? bin64_t(bin64_t::NONE) : peaks_[pi];
-}
-
-
-void FileTransfer::OfferHash (bin64_t pos, const Sha1Hash& hash) {
- if (!size_) // only peak hashes are accepted at this point
- return OfferPeak(pos,hash);
- int pi=0;
- while (pi<peak_count_ && !pos.within(peaks_[pi]))
- pi++;
- if (pi==peak_count_)
- return;
- if (pos==peaks_[pi] && hash!=peak_hashes_[pi])
- return;
- else if (ack_out_.get(pos.parent())!=bins::EMPTY)
- return; // have this hash already, even accptd data
- hashes_[pos] = hash;
-}
-
-
-bool FileTransfer::OfferData (bin64_t pos, const uint8_t* data, size_t length) {
- if (!pos.is_base())
- return false;
- if (length<1024 && pos!=bin64_t(0,sizek_-1))
- return false;
- if (ack_out_.get(pos)==bins::FILLED)
- return true; // ???
- bin64_t peak = peak_for(pos);
- if (peak==bin64_t::NONE)
- return false;
-
- Sha1Hash hash(data,length);
- bin64_t p = pos;
- while ( p!=peak && ack_out_.get(p)==bins::EMPTY ) {
- hashes_[p] = hash;
- p = p.parent();
- hash = Sha1Hash(hashes_[p.left()],hashes_[p.right()]) ;
- }
- if (hash!=hashes_[p])
- return false;
-
- //printf("g %lli %s\n",(uint64_t)pos,hash.hex().c_str());
- // walk to the nearest proven hash FIXME 0-layer peak
- OnDataIn(pos);
- pwrite(fd_,data,length,pos.base_offset()<<10);
- complete_ += length;
- completek_++;
- if (length<1024) {
- size_ -= 1024 - length;
- ftruncate(fd_, size_);
- }
- while (ack_out_.get(bin64_t(0,seq_complete_>>10))==bins::FILLED)
- seq_complete_+=1024;
- if (seq_complete_>size_)
- seq_complete_ = size_;
- return true;
-}
-
-
-/*bin64_t FileTransfer::RevealAck (uint64_t& offset) {
- if (offset<data_in_off_)
- offset = data_in_off_;
- for(int off=offset-data_in_off_; off<data_in_.size(); off++) {
- offset++;
- if (data_in_[off]!=bin64_t::NONE) {
- bin64_t parent = data_in_[off].parent();
- if (ack_out_.get(parent)!=bins::FILLED)
- return data_in_[off];
- else
- data_in_[off] = bin64_t::NONE;
- }
- }
- return bin64_t::NONE;
-}*/
-
-
-void FileTransfer::OnDataIn (bin64_t pos) {
- ack_out_.set(pos,bins::FILLED);
- /*bin64_t closed = pos;
- while (ack_out_.get(closed.parent())==bins::FILLED) // TODO optimize
- closed = closed.parent();
- data_in_.push_back(closed);
- // rotating the queue
- bin64_t parent = data_in_.front().parent();
- if (ack_out_.get(parent)!=bins::FILLED)
- data_in_.push_back(data_in_.front());
- data_in_.front() = bin64_t::NONE;
- while ( !data_in_.empty() && data_in_.front()==bin64_t::NONE) {
- data_in_.pop_front();
- data_in_off_++;
- }*/
-}
-
-
-Sha1Hash FileTransfer::DeriveRoot () {
- int c = peak_count_-1;
- bin64_t p = peaks_[c];
- Sha1Hash hash = peak_hashes_[c];
- c--;
- while (p!=bin64_t::ALL) {
- if (p.is_left()) {
- p = p.parent();
- hash = Sha1Hash(hash,Sha1Hash::ZERO);
- } else {
- if (c<0 || peaks_[c]!=p.sibling())
- return Sha1Hash::ZERO;
- hash = Sha1Hash(peak_hashes_[c],hash);
- p = p.parent();
- c--;
- }
- //printf("p %lli %s\n",(uint64_t)p,hash.hex().c_str());
- }
- return hash;
-}
-
-
-void FileTransfer::OfferPeak (bin64_t pos, const Sha1Hash& hash) {
- assert(!size_);
- if (peak_count_) {
- bin64_t last_peak = peaks_[peak_count_-1];
- if ( pos.layer()>=last_peak.layer() ||
- pos.base_offset()!=last_peak.base_offset()+last_peak.width() )
- peak_count_ = 0;
- }
- peaks_[peak_count_] = pos;
- peak_hashes_[peak_count_++] = hash;
- // check whether peak hash candidates add up to the root hash
- Sha1Hash mustbe_root = DeriveRoot();
- if (mustbe_root!=root_hash_)
- return;
- // bingo, we now know the file size (rounded up to a KByte)
- SetSize( (pos.base_offset()+pos.width()) << 10 );
- SavePeaks();
-}
-
FileTransfer::~FileTransfer ()
{
-#ifdef _WIN32
- UnmapViewOfFile(hashes_);
- CloseHandle(hashmaphandle_);
-#else
- munmap(hashes_,sizek_*2*Sha1Hash::SIZE);
- close(hashfd_);
- close(fd_);
- files[fd_] = NULL;
-#endif
+
+ files[fd()] = NULL;
}
}
-std::string FileTransfer::GetTempFilename(Sha1Hash& root_hash, int instance, std::string postfix)
-{
- std::string tempfile = gettmpdir();
- std::stringstream ss;
- ss << instance;
- tempfile += std::string(".") + root_hash.hex() + std::string(".") + ss.str() + postfix;
- return tempfile;
-}
-
-
-/*int p2tp::Open (const char* filename, const Sha1Hash& hash) {
- FileTransfer* ft = new FileTransfer(filename, hash);
- int fdes = ft->file_descriptor();
- if (fdes>0) {
- if (FileTransfer::files.size()<fdes)
- FileTransfer::files.resize(fdes);
- FileTransfer::files[fdes] = ft;
- return fdes;
- } else {
- delete ft;
- return -1;
- }
-}*/
-
-
void FileTransfer::OnPexIn (const Address& addr) {
for(int i=0; i<hs_in_.size(); i++) {
Channel* c = Channel::channels[hs_in_[i]];
- if (c && c->file_==this && c->peer_==addr)
+ if (c && c->transfer().fd()==this->fd() && c->peer_==addr)
return; // already connected
}
if (hs_in_.size()<20) {
pex_out_ = 0;
while (pex_out_<hs_in_.size()) {
Channel* c = Channel::channels[hs_in_[pex_out_]];
- if (c && c->file_==this) {
+ if (c && c->transfer().fd()==this->fd()) {
pex_out_ += hs_in_offset_ + 1;
return c->id;
} else {
return -1;
}
-
-/*
- for(int i=0; i<peak_hash_count; i++) {
- bin64_t x = peaks[i], end = x.sibling();
- do {
- while (!x.layer()>10) {
- OfferHash(x.right(), hashes[x.right()]);
- if ( ! OfferHash(x.left(), hashes[x.left()]) )
- break;
- x = x.left();
- }
-
- if (x.layer()==10) {
- if (recheck_data) {
- uint8_t data[1024];
- size_t rd = pread(fd,data,2<<10,x.base_offset());
- if (hashes[x]==Sha1Hash(data,rd))
- ack_out->set(x,bins::FILLED);
- // may avoid hashing by checking whether it is zero
- // and whether the hash matches hash of zero
- } else {
- ack_out->set(x,bins::FILLED);
- }
- }
-
- while (x.is_right() && x!=peaks[i])
- x = x.parent();
- x = x.sibling();
- } while (x!=end);
- }
-
-
-
-
- // open file
- if ( hash_file_st.st_size < (sizeof(bin64_t)+Sha1Hash::SIZE)*64 )
- return;
- // read root hash
- char hashbuf[128];
- uint64_t binbuf;
- lseek(hashfd_,0,SEEK_SET);
- read(hashfd_,&binbuf,sizeof(bin64_t));
- read(hashfd_,hashbuf,Sha1Hash::SIZE);
- Sha1Hash mustberoot(false,(const char*)hashbuf);
- if ( binbuf!=bin64_t::ALL || mustberoot != this->root_hash ) {
- ftruncate(hashfd_,Sha1Hash::SIZE*64);
- return;
- }
- // read peak hashes
- for(int i=1; i<64 && !this->size; i++){
- read(hashfd_,&binbuf,sizeof(bin64_t));
- read(hashfd_,hashbuf,Sha1Hash::SIZE);
- Sha1Hash mustbepeak(false,(const char*)hashbuf);
- if (mustbepeak==Sha1Hash::ZERO)
- break;
- OfferPeak(binbuf,mustbepeak);
- }
- if (!size)
- return;
-
-
- */