diff --git a/doc/lftp.1 b/doc/lftp.1 index d5d408f..9222795 100644 --- a/doc/lftp.1 +++ b/doc/lftp.1 @@ -1748,10 +1748,15 @@ is applied. It is never used if mirror:exclude-regex is empty. .BR mirror:no-empty-dirs " (boolean)" when true, mirror doesn't create empty directories (like \-\-no\-empty\-dirs option). .TP +.BR mirror:sort-by " (string)" +specifies order of file transfers. Valid values are: name, name-desc, size, size-desc, +date, date-desc. When the value is name or name-desc, then mirror:order setting also +affects the order or transfers. +.TP .BR mirror:order " (list of patterns)" -specifies order of file transfers. E.g. setting this to "*.sfv *.sum" makes mirror to +specifies order of file transfers when sorting by name. E.g. setting this to "*.sfv *.sum" makes mirror to transfer files matching *.sfv first, then ones matching *.sum and then all other -files. To process directories after other files, add "*/" to end of pattern list. +files. To process directories after other files, add "*/" to the end of pattern list. .TP .BR mirror:parallel-directories " (boolean)" if true, mirror will start processing of several directories in parallel diff --git a/po/pl.po b/po/pl.po index d569428..89e8b11 100644 --- a/po/pl.po +++ b/po/pl.po @@ -7,10 +7,10 @@ # msgid "" msgstr "" -"Project-Id-Version: lftp 4.4.16\n" +"Project-Id-Version: lftp 4.5.3\n" "Report-Msgid-Bugs-To: lftp-bugs@lftp.yar.ru\n" "POT-Creation-Date: 2014-07-06 16:15+0400\n" -"PO-Revision-Date: 2014-05-18 06:54+0200\n" +"PO-Revision-Date: 2014-07-13 14:15+0200\n" "Last-Translator: Jakub Bogusz \n" "Language-Team: Polish \n" "Language: pl\n" @@ -1152,12 +1152,10 @@ msgstr "" "uczyni dopełnianie nazw plików nie rozpoznającym wielkości liter.\n" #: src/commands.cc:216 -#, fuzzy msgid "debug [OPTS] [|off]" -msgstr "debug [|off] [-o ]" +msgstr "debug [OPCJE] [|off]" #: src/commands.cc:217 -#, fuzzy msgid "" "Set debug level to given value or turn debug off completely.\n" " -o redirect debug output to the file\n" @@ -1165,8 +1163,11 @@ msgid "" " -p show PID\n" " -t show timestamps\n" msgstr "" -"Ustaw poziom debugowania na daną wartość lub wyłącz zupełnie.\n" -" -o przekieruj wyjście debugowania do pliku.\n" +"Ustaw poziom diagnostyki na daną wartość lub wyłącz zupełnie.\n" +" -o przekieruj wyjście diagnostyki do pliku.\n" +" -c wypisuj kontekst komunikatu\n" +" -p wypisuj PID\n" +" -t wypisuj znaczniki czasu\n" #: src/commands.cc:222 msgid "du [options] " @@ -1477,12 +1478,10 @@ msgstr "" "podano nazw obu katalogów, to będą użyte bieżące katalogi lokalny i zdalny.\n" #: src/commands.cc:339 -#, fuzzy msgid "mkdir [OPTS] " -msgstr "mkdir [-p] " +msgstr "mkdir [OPCJE] " #: src/commands.cc:340 -#, fuzzy msgid "" "Make remote directories\n" " -p make all levels of path\n" @@ -1490,6 +1489,7 @@ msgid "" msgstr "" "Utwórz zdalne katalogi\n" " -p tworzy wszystkie poziomy ścieżki\n" +" -f ciche działanie, bez komunikatów\n" #: src/commands.cc:343 msgid "module name [args]" @@ -2378,9 +2378,9 @@ msgid "unsupported network protocol" msgstr "protokół nie jest obsługiwany" #: src/ftpclass.cc:2133 src/ftpclass.cc:2228 -#, fuzzy, c-format +#, c-format msgid "Data socket error (%s) - reconnecting" -msgstr "Błąd połączenia (%s) - powtórne łączenie" +msgstr "Błąd gniazda danych (%s) - powtórne łączenie" #: src/ftpclass.cc:2161 #, c-format @@ -2700,13 +2700,12 @@ msgid "peer unexpectedly closed connection after %s" msgstr "druga strona nieoczekiwanie zamknęła połączenie po %s" #: src/Torrent.cc:2903 src/Torrent.cc:3020 -#, fuzzy msgid "peer unexpectedly closed connection" -msgstr "druga strona nieoczekiwanie zamknęła połączenie po %s" +msgstr "druga strona nieoczekiwanie zamknęła połączenie" #: src/Torrent.cc:2905 src/Torrent.cc:2906 msgid "peer closed connection (before handshake)" -msgstr "druga strona zamknęła połączenie (przed jego ustanowieniem)" +msgstr "druga strona zamknęła połączenie (przed przywitaniem)" #: src/Torrent.cc:2909 src/Torrent.cc:3022 src/Torrent.cc:3023 msgid "invalid peer response format" @@ -2732,15 +2731,15 @@ msgstr "Przyjęto połączenie od [%s]:%d" #: src/Torrent.cc:3552 #, c-format msgid "peer sent unknown info_hash=%s in handshake" -msgstr "" +msgstr "druga strona wysłała nieznane info_hash=%s przy przywitaniu" #: src/Torrent.cc:3576 msgid "peer handshake timeout" -msgstr "upłynął limit czasu nawiązywania połączenia z drugą stroną" +msgstr "upłynął limit czasu przywitania z drugą stroną" #: src/Torrent.cc:3588 msgid "peer short handshake" -msgstr "nawiązanie połączenia z drugą stroną zostało ucięte" +msgstr "przywitanie z drugą stroną zostało ucięte" #: src/Torrent.cc:3590 msgid "peer closed just accepted connection" diff --git a/src/FileSet.cc b/src/FileSet.cc index 389578a..6380a68 100644 --- a/src/FileSet.cc +++ b/src/FileSet.cc @@ -232,6 +232,21 @@ void FileSet::Sort(sort_e newsort, bool casefold, bool reverse) } } +// reverse current sort order +void FileSet::ReverseSort() +{ + if(!sorted) { + Sort(BYNAME,false,true); + return; + } + int i=0; + int j=sorted.length()-1; + while(iEventTime()); if(recv_buf) timeout_timer.Reset(recv_buf->EventTime()); + if(pty_send_buf) + timeout_timer.Reset(pty_send_buf->EventTime()); + if(pty_recv_buf) + timeout_timer.Reset(pty_recv_buf->EventTime()); + + // check for timeout only if there should be connection activity. + if(state!=DISCONNECTED && (state!=CONNECTED || !RespQueueIsEmpty()) + && mode!=CLOSED && CheckTimeout()) + return MOVED; if((state==FILE_RECV || state==FILE_SEND) && rate_limit==0) @@ -275,14 +284,6 @@ int Fish::Do() case DONE: break; } - if(m==MOVED) - return MOVED; - if(send_buf) - timeout_timer.Reset(send_buf->EventTime()); - if(recv_buf) - timeout_timer.Reset(recv_buf->EventTime()); - if(CheckTimeout()) - return MOVED; return m; } diff --git a/src/MirrorJob.cc b/src/MirrorJob.cc index a19ff8e..eb148f1 100644 --- a/src/MirrorJob.cc +++ b/src/MirrorJob.cc @@ -589,10 +589,16 @@ void MirrorJob::InitSets(const FileSet *source,const FileSet *dest) if(!(flags&DELETE)) to_transfer->SubtractAny(to_rm_mismatched); - to_transfer->SortByPatternList(ResMgr::Query("mirror:order",0)); - to_transfer->CountBytes(&bytes_to_transfer); - if(parent_mirror) - parent_mirror->AddBytesToTransfer(bytes_to_transfer); + const char *sort_by=ResMgr::Query("mirror:sort-by",0); + bool desc=strstr(sort_by,"-desc"); + if(!strncmp(sort_by,"name",4)) + to_transfer->SortByPatternList(ResMgr::Query("mirror:order",0)); + else if(!strncmp(sort_by,"date",4)) + to_transfer->Sort(FileSet::BYDATE); + else if(!strncmp(sort_by,"size",4)) + to_transfer->Sort(FileSet::BYSIZE,false,true); + if(desc) + to_transfer->ReverseSort(); int dir_count=0; to_transfer->Count(&dir_count,NULL,NULL,NULL); @@ -851,10 +857,16 @@ int MirrorJob::Do() stats.dirs++; InitSets(source_set,target_set); + + to_transfer->CountBytes(&bytes_to_transfer); + if(parent_mirror) + parent_mirror->AddBytesToTransfer(bytes_to_transfer); + to_rm->Count(&stats.del_dirs,&stats.del_files,&stats.del_symlinks,&stats.del_files); to_rm->rewind(); to_rm_mismatched->Count(&stats.del_dirs,&stats.del_files,&stats.del_symlinks,&stats.del_files); to_rm_mismatched->rewind(); + set_state(TARGET_REMOVE_OLD_FIRST); goto TARGET_REMOVE_OLD_FIRST_label; diff --git a/src/PollVec.cc b/src/PollVec.cc index a18b517..e1d72fb 100644 --- a/src/PollVec.cc +++ b/src/PollVec.cc @@ -31,7 +31,7 @@ static inline bool operator<(const timeval& a,const timeval& b) void PollVec::AddTimeoutU(unsigned t) { struct timeval new_timeout={t/1000000,t%1000000}; - if(new_timeout ProcWait::all_proc; + +const xstring& ProcWait::proc_key(pid_t p) +{ + static xstring tmp_key; + tmp_key.nset((const char*)&p,sizeof(p)); + return tmp_key; +} int ProcWait::Do() { @@ -100,27 +107,19 @@ int ProcWait::Kill(int sig) } ProcWait::ProcWait(pid_t p) + : pid(p) { auto_die=false; - pid=p; status=RUNNING; term_info=-1; saved_errno=0; - next=chain; - chain=this; + all_proc.add(proc_key(pid),this); } ProcWait::~ProcWait() { - for(ProcWait **scan=&chain; *scan; scan=&(*scan)->next) - { - if(*scan==this) - { - *scan=next; - return; - } - } + all_proc.remove(proc_key(pid)); } void ProcWait::SIGCHLD_handler(int sig) @@ -130,16 +129,9 @@ void ProcWait::SIGCHLD_handler(int sig) pid_t pp=waitpid(-1,&info,WUNTRACED|WNOHANG); if(pp==-1) return; - for(ProcWait *scan=chain; scan; scan=scan->next) - { - if(scan->pid==pp) - { - scan->handle_info(info); - return; - } - } - // no WaitProc for the pid. Probably the process died too fast, - // but next waitpid should take care of it. + ProcWait *w=all_proc.lookup(proc_key(pp)); + if(w && w->handle_info(info)) + w->Timeout(0); } void ProcWait::Signal(bool yes) @@ -156,6 +148,6 @@ void ProcWait::Signal(bool yes) void ProcWait::DeleteAll() { Signal(false); - for(ProcWait *scan=chain; scan; scan=scan->next) - Delete(scan); + for(ProcWait *w=all_proc.each_begin(); w; w=all_proc.each_next()) + Delete(w); } diff --git a/src/ProcWait.h b/src/ProcWait.h index d6f0bad..651bc21 100644 --- a/src/ProcWait.h +++ b/src/ProcWait.h @@ -23,6 +23,7 @@ #include #include #include "SMTask.h" +#include "xmap.h" class ProcWait : public SMTask { @@ -35,10 +36,10 @@ public: }; protected: - static ProcWait *chain; - ProcWait *next; + static xmap all_proc; + static const xstring& proc_key(pid_t p); // make key for xmap - pid_t pid; + const pid_t pid; State status; int term_info; int saved_errno; diff --git a/src/Ref.h b/src/Ref.h index 8731089..c96ee96 100644 --- a/src/Ref.h +++ b/src/Ref.h @@ -51,4 +51,22 @@ public: template const Ref Ref::null; +template class RefToArray : public Ref +{ + RefToArray(const RefToArray&); // disable cloning + void operator=(const RefToArray&); // and assignment + +public: + RefToArray() {} + RefToArray(T *p) : Ref(p) {} + ~RefToArray() { delete[] Ref::ptr; Ref::ptr=0; } + void operator=(T *p) { delete[] Ref::ptr; Ref::ptr=p; } + T& operator[](unsigned i) const { return Ref::ptr[i]; } + + static const RefToArray null; +}; + +template const RefToArray RefToArray::null; + + #endif diff --git a/src/SFtp.cc b/src/SFtp.cc index 8e6d0d5..3225701 100644 --- a/src/SFtp.cc +++ b/src/SFtp.cc @@ -915,11 +915,7 @@ void SFtp::HandleExpect(Expect *e) for(int i=0; iGetCount(); i++) { const NameAttrs *a=r->GetNameAttrs(i); - if(!file_set) - file_set=new FileSet; FileInfo *info=MakeFileInfo(a); - if(info) - file_set->Add(info); if(mode==LIST) { file_buf->Put(a->name); @@ -941,6 +937,12 @@ void SFtp::HandleExpect(Expect *e) file_buf->Put("\n"); } } + if(info) + { + if(!file_set) + file_set=new FileSet; + file_set->Add(info); + } } if(r->Eof()) goto eof; diff --git a/src/Torrent.cc b/src/Torrent.cc index ffe6433..270bbd6 100644 --- a/src/Torrent.cc +++ b/src/Torrent.cc @@ -278,6 +278,10 @@ Torrent::Torrent(const char *mf,const char *c,const char *od) dht_announce_timer.Stop(); } +Torrent::~Torrent() +{ +} + bool Torrent::TrackersDone() const { if(shutting_down && shutting_down_timer.Stopped()) @@ -318,6 +322,8 @@ void Torrent::PrepareToDie() RemoveTorrent(this); if(GetTorrentsCount()==0) { StopListener(); + StopDHT(); + StopListenerUDP(); fd_cache=0; black_list=0; } @@ -348,9 +354,7 @@ double Torrent::GetRatio() const void Torrent::SetDownloader(unsigned piece,unsigned block,const TorrentPeer *o,const TorrentPeer *n) { - const TorrentPeer*& downloader=piece_info[piece]->downloader[block]; - if(downloader==o) - downloader=n; + piece_info[piece].set_downloader(block,o,n,BlocksInPiece(piece)); } BeNode *Torrent::Lookup(xmap_p& dict,const char *name,BeNode::be_type_t type) @@ -421,25 +425,18 @@ void Torrent::ValidatePiece(unsigned p) complete_pieces--; my_bitfield->set_bit(p,0); } - piece_info[p]->block_map.clear(); + SetBlocksAbsent(p); } else { LogNote(11,"piece %u ok",p); if(!my_bitfield->get_bit(p)) { total_left-=PieceLength(p); complete_pieces++; my_bitfield->set_bit(p,1); + piece_info[p].free_block_map(); } } } -bool TorrentPiece::has_a_downloader() const -{ - for(int i=0; i static inline int cmp(T a,T b) { @@ -453,8 +450,8 @@ static inline int cmp(T a,T b) static Torrent *cmp_torrent; int Torrent::PiecesNeededCmp(const unsigned *a,const unsigned *b) { - int ra=cmp_torrent->piece_info[*a]->sources_count; - int rb=cmp_torrent->piece_info[*b]->sources_count; + int ra=cmp_torrent->piece_info[*a].get_sources_count(); + int rb=cmp_torrent->piece_info[*b].get_sources_count(); int c=cmp(ra,rb); if(c) return c; return cmp(*a,*b); @@ -721,7 +718,7 @@ void Torrent::SetMetadata(const xstring& md) } BeNode *b_piece_length=Lookup(info,"piece length",BeNode::BE_INT); - if(!b_piece_length) + if(!b_piece_length || b_piece_length->num<1024 || b_piece_length->num>INT_MAX/4) return; piece_length=b_piece_length->num; LogNote(4,"Piece length is %u",piece_length); @@ -744,7 +741,7 @@ void Torrent::SetMetadata(const xstring& md) if(!files) { single_file=true; BeNode *length=Lookup(info,"length",BeNode::BE_INT); - if(!length) + if(!length || length->num<0) return; total_length=length->num; } else { @@ -764,9 +761,12 @@ void Torrent::SetMetadata(const xstring& md) return; if(!Lookup(files->list[i]->dict,"path",BeNode::BE_LIST)) return; + if(f->num<0) + return; total_length+=f->num; } } + this->files=new TorrentFiles(files,this); LogNote(4,"Total length is %llu",total_length); total_left=total_length; @@ -800,16 +800,18 @@ void Torrent::SetMetadata(const xstring& md) SaveMetadata(); my_bitfield=new BitField(total_pieces); - for(unsigned p=0; pset_bit(i,1); + my_bitfield->set_range(0,total_pieces,1); complete_pieces=total_pieces; total_left=0; complete=true; @@ -927,7 +929,7 @@ void Torrent::CalcPiecesStats() for(unsigned i=0; iget_bit(i)) continue; - unsigned sc=piece_info[i]->sources_count; + unsigned sc=piece_info[i].get_sources_count(); if(min_piece_sources>sc) min_piece_sources=sc; if(sc==0) @@ -945,12 +947,13 @@ void Torrent::RebuildPiecesNeeded() bool enter_end_game=true; for(unsigned i=0; iget_bit(i)) { - if(!piece_info[i]->has_a_downloader()) + if(!piece_info[i].has_a_downloader()) enter_end_game=false; - if(piece_info[i]->sources_count==0) + if(piece_info[i].has_no_sources()) continue; pieces_needed.append(i); } + piece_info[i].cleanup(); } if(!end_game && enter_end_game) { LogNote(1,"entering End Game mode"); @@ -1084,6 +1087,8 @@ int Torrent::Do() a.sa.sa_family=AF_INET6; if(inet_pton(AF_INET6,b_ip->str,&a.in6.sin6_addr)<=0) continue; + if(b_port->num<=0 || b_port->num>=0x10000) + continue; a.set_port(b_port->num); dht_ipv6->SendPing(a); } else @@ -1092,6 +1097,8 @@ int Torrent::Do() a.sa.sa_family=AF_INET; if(!inet_aton(b_ip->str,&a.in.sin_addr)) continue; + if(b_port->num<=0 || b_port->num>=0x10000) + continue; a.set_port(b_port->num); dht->SendPing(a); } @@ -1249,24 +1256,51 @@ const char *Torrent::MakePath(BeNode *p) const } const char *Torrent::FindFileByPosition(unsigned piece,unsigned begin,off_t *f_pos,off_t *f_tail) const { - const BeNode *files=info->lookup("files",BeNode::BE_LIST); off_t target_pos=(off_t)piece*piece_length+begin; + TorrentFile *file=files->FindByPosition(target_pos); + if(!file) + return 0; + + *f_pos=target_pos-file->pos; + *f_tail=file->length-*f_pos; + + return file->path; +} + +TorrentFiles::TorrentFiles(const BeNode *files,const Torrent *t) +{ if(!files) { - *f_pos=target_pos; - *f_tail=total_length-target_pos; - return name; + grow_space(1); + set_length(1); + file(0)->set(t->GetName(),0,t->TotalLength()); } else { + int count=files->list.length(); + grow_space(count); + set_length(count); off_t scan_pos=0; - for(int i=0; ilist.length(); i++) { - off_t file_length=files->list[i]->lookup_int("length"); - if(scan_pos<=target_pos && scan_pos+file_length>target_pos) { - *f_pos=target_pos-scan_pos; - *f_tail=file_length-*f_pos; - return MakePath(files->list[i]); - } + for(int i=0; ilist[i]; + off_t file_length=node->lookup_int("length"); + file(i)->set(t->MakePath(node),scan_pos,file_length); scan_pos+=file_length; } } + qsort(pos_cmp); +} +TorrentFile *TorrentFiles::FindByPosition(off_t pos) +{ + int i=0; + int j=length()-1; + while(i<=j) { + // invariant: the target element is in the range [i,j] + int m=(i+j)/2; + if(file(m)->contains_pos(pos)) + return file(m); + if(file(m)->pos>pos) + j=m-1; + else + i=m+1; + } return 0; } @@ -1485,9 +1519,9 @@ void Torrent::StoreBlock(unsigned piece,unsigned begin,unsigned len,const char * } while(bc-->0) { - piece_info[piece]->block_map.set_bit(b++,1); + SetBlockPresent(piece,b++); } - if(piece_info[piece]->block_map.has_all_set() && !my_bitfield->get_bit(piece)) { + if(AllBlocksPresent(piece) && !my_bitfield->get_bit(piece)) { ValidatePiece(piece); if(!my_bitfield->get_bit(piece)) { LogError(0,"new piece %u digest mismatch",piece); @@ -2021,12 +2055,12 @@ int TorrentPeer::SendDataRequests(unsigned p) unsigned blocks=parent->BlocksInPiece(p); unsigned bytes_allowed=BytesAllowed(RateLimit::GET); for(unsigned b=0; bpiece_info[p]->block_map.get_bit(b)) + if(parent->BlockPresent(p,b)) continue; - if(parent->piece_info[p]->downloader[b]) { + if(parent->piece_info[p].downloader_for(b)) { if(!parent->end_game) continue; - if(parent->piece_info[p]->downloader[b]==this) + if(parent->piece_info[p].downloader_for(b)==this) continue; if(FindRequest(p,b*Torrent::BLOCK_SIZE)>=0) continue; @@ -2118,8 +2152,7 @@ void TorrentPeer::SendDataRequests() if(parent->my_bitfield->get_bit(p)) continue; // add some randomness, so that different instances don't synchronize - if(!parent->piece_info[p]->block_map.has_any_set() - && random()/13%16==0) + if(parent->AllBlocksAbsent(p) && random()/13%16==0) continue; if(SendDataRequests(p)>0) return; @@ -2233,7 +2266,7 @@ unsigned TorrentPeer::GetLastPiece() const unsigned p=last_piece; // continue if have any blocks already if(p!=NO_PIECE && !parent->my_bitfield->get_bit(p) - && parent->piece_info[p]->block_map.has_any_set() + && parent->AnyBlocksPresent(p) && peer_bitfield->get_bit(p)) return p; p=parent->last_piece; @@ -2302,11 +2335,11 @@ void TorrentPeer::SetPieceHaving(unsigned p,bool have) int diff = (have - peer_bitfield->get_bit(p)); if(!diff) return; - parent->piece_info[p]->sources_count+=diff; + parent->piece_info[p].add_sources_count(diff); peer_complete_pieces+=diff; peer_bitfield->set_bit(p,have); - if(parent->piece_info[p]->sources_count==0) + if(parent->piece_info[p].get_sources_count()==0) parent->SetPieceNotWanted(p); if(have && send_buf && !am_interested && !parent->my_bitfield->get_bit(p) && parent->NeedMoreUploaders()) { @@ -2641,7 +2674,7 @@ void TorrentPeer::HandleExtendedMessage(PacketExtended *pp) return; } BeNode *piece=pp->data->lookup("piece",BeNode::BE_INT); - if(!piece) { + if(!piece || piece->num<0 || piece->num>=INT_MAX/Torrent::BLOCK_SIZE) { SetError("ut_metadata piece bad or missing"); return; } @@ -2692,6 +2725,9 @@ void TorrentPeer::HandleExtendedMessage(PacketExtended *pp) } case UT_METADATA_REJECT: break; + default: + SetError("ut_metadata msg_type invalid value"); + return; } } else if(pp->code==MSG_EXT_PEX) { if(!pex.recv_timer.Stopped()) @@ -3322,14 +3358,16 @@ bool BitField::has_all_set(int from,int to) const { return false; return true; } - +void BitField::set_range(int from,int to,bool value) { + for(int i=from; iStopped()) { Log::global->Format(4,"---- black-delisting peer %s\n",bl.each_key().get()); - delete e; bl.remove(bl.each_key()); } } diff --git a/src/Torrent.h b/src/Torrent.h index a9943c6..ef5d75c 100644 --- a/src/Torrent.h +++ b/src/Torrent.h @@ -53,20 +53,105 @@ public: int get_bit_length() const { return bit_length; } void set_bit_length(int b) { bit_length=b; set_length((b+7)/8); } void clear() { memset(buf,0,length()); } + void set_range(int from,int to,bool value); }; -struct TorrentPiece +class TorrentPiece { unsigned sources_count; // how many peers have the piece + unsigned downloader_count; // how many downloaders of the piece are there + RefToArray downloader; // which peers download the blocks + Ref block_map; // which blocks are present. - BitField block_map; // which blocks are present - xarray downloader; // which peers download the blocks +public: + TorrentPiece() : sources_count(0), downloader_count(0) {} + ~TorrentPiece() {} + + unsigned get_sources_count() const { return sources_count; } + void add_sources_count(int diff) { sources_count+=diff; } + bool has_no_sources() const { return sources_count==0; } + + bool has_a_downloader() const { return downloader_count>0; } + void set_downloader(unsigned block,const TorrentPeer *o,const TorrentPeer *n,unsigned blk_count) { + if(!downloader) { + if(o || !n) + return; + downloader=new const TorrentPeer*[blk_count](); + } + const TorrentPeer*& d=downloader[block]; + if(d==o) { + d=n; + downloader_count+=(n!=0)-(o!=0); + } + } + void cleanup() { + if(downloader_count==0 && downloader) + downloader=0; + } + const TorrentPeer *downloader_for(unsigned block) { + return downloader ? downloader[block] : 0; + } - TorrentPiece(unsigned b) - : sources_count(0), block_map(b) - { downloader.allocate(b,0); } + void set_block_present(unsigned block,unsigned blk_count) { + if(!block_map) + block_map=new BitField(blk_count); + block_map->set_bit(block,1); + } + void set_blocks_absent() { + block_map=0; + } + void free_block_map() { + block_map=0; + } + bool block_present(unsigned block) const { + return block_map && block_map->get_bit(block); + } + bool all_blocks_present(unsigned blk_count) const { + return block_map && block_map->has_all_set(0,blk_count); + } + bool any_blocks_present() const { + return block_map; // it's allocated when setting any bit + } +}; + +struct TorrentFile +{ + char *path; + off_t pos; + off_t length; + void set(const char *n,off_t p,off_t l) { + path=xstrdup(n); + pos=p; + length=l; + } + void unset() { + xfree(path); path=0; + } + bool contains_pos(off_t p) const { + return p>=pos && p +{ + static int pos_cmp(const TorrentFile *a, const TorrentFile *b) { + if(a->pos < b->pos) + return -1; + if(a->pos > b->pos) + return 1; + // we want zero-sized files to placed before non-zero ones. + if(a->length != b->length) + return a->length < b->length ? -1 : 1; + return 0; + } +public: + TorrentFile *file(int i) { return get_non_const()+i; } + TorrentFiles(const BeNode *f_node,const Torrent *t); + ~TorrentFiles() { + for(int i=0; iunset(); + } + TorrentFile *FindByPosition(off_t p); }; class TorrentListener : public SMTask, protected ProtoLog, protected Networker @@ -99,6 +184,7 @@ class Torrent : public SMTask, protected ProtoLog, public ResClient friend class TorrentPeer; friend class TorrentDispatcher; friend class TorrentListener; + friend class TorrentFiles; friend class DHT; bool shutting_down; @@ -171,6 +257,7 @@ class Torrent : public SMTask, protected ProtoLog, public ResClient xstring info_hash; const xstring *pieces; xstring name; + Ref files; Ref recv_translate; Ref recv_translate_utf8; @@ -214,11 +301,32 @@ class Torrent : public SMTask, protected ProtoLog, public ResClient BeNode *Lookup(Ref& d,const char *name,BeNode::be_type_t type) { return Lookup(d->dict,name,type); } TaskRefArray peers; - RefArray piece_info; static int PeersCompareActivity(const SMTaskRef *p1,const SMTaskRef *p2); static int PeersCompareRecvRate(const SMTaskRef *p1,const SMTaskRef *p2); static int PeersCompareSendRate(const SMTaskRef *p1,const SMTaskRef *p2); + RefToArray piece_info; + unsigned blocks_in_piece; + unsigned blocks_in_last_piece; + bool BlockPresent(unsigned piece,unsigned block) const { + return piece_info[piece].block_present(block); + } + bool AllBlocksPresent(unsigned piece) const { + return piece_info[piece].all_blocks_present(BlocksInPiece(piece)); + } + bool AnyBlocksPresent(unsigned piece) const { + return piece_info[piece].any_blocks_present(); + } + bool AllBlocksAbsent(unsigned piece) const { + return !AnyBlocksPresent(piece); + } + void SetBlocksAbsent(unsigned piece) { + piece_info[piece].set_blocks_absent(); + } + void SetBlockPresent(unsigned piece,unsigned block) { + piece_info[piece].set_block_present(block,BlocksInPiece(piece)); + } + void RebuildPiecesNeeded(); Timer pieces_needed_rebuild_timer; xarray pieces_needed; @@ -296,6 +404,7 @@ public: static void ClassInit(); Torrent(const char *mf,const char *cwd,const char *output_dir); + ~Torrent(); int Do(); int Done() const; @@ -315,7 +424,7 @@ public: static void SHA1(const xstring& str,xstring& buf); void ValidatePiece(unsigned p); unsigned PieceLength(unsigned p) const { return p==total_pieces-1 ? last_piece_length : piece_length; } - unsigned BlocksInPiece(unsigned p) const { return (PieceLength(p)+BLOCK_SIZE-1)/BLOCK_SIZE; } + unsigned BlocksInPiece(unsigned p) const { return p==total_pieces-1 ? blocks_in_last_piece : blocks_in_piece; } const TaskRefArray& GetPeers() const { return peers; } void AddPeer(TorrentPeer *); @@ -772,7 +881,7 @@ public: class TorrentBlackList { - xmap bl; + xmap_p bl; void check_expire(); public: bool Listed(const sockaddr_u &a); diff --git a/src/resource.cc b/src/resource.cc index 4aa071a..e4707e7 100644 --- a/src/resource.cc +++ b/src/resource.cc @@ -57,13 +57,39 @@ static const char *FtpProxyValidate(xstring_c *p) return 0; } +static const char *SetValidate(xstring_c& s,const char *const *set,const char *name) +{ + const char *const *scan; + for(scan=set; *scan; scan++) + if(s.eq(*scan)) + return 0; + + xstring &j=xstring::get_tmp(); + if(name) + j.setf(_("%s must be one of: "),name); + else + j.set(_("must be one of: ")); + bool had_empty=false; + for(scan=set; *scan; scan++) { + if(!**scan) { + had_empty=true; + continue; + } + if(scan>set) + j.append(", "); + j.append(*scan); + } + if(had_empty) + j.append(_(", or empty")); + return j; +} + static const char *FtpProxyAuthTypeValidate(xstring_c *s) { - if(s->ne("user") && s->ne("joined") && s->ne("joined-acct") - && s->ne("open") && s->ne("proxy-user@host")) - // for translator: `user', `joined', `joined-acct', `open' are literals. - return _("ftp:proxy-auth-type must be one of: user, joined, joined-acct, open, proxy-user@host"); - return 0; + static const char *const valid_set[]={ + "user", "joined", "joined-acct", "open", "proxy-user@host", 0 + }; + return SetValidate(*s,valid_set,"ftp:proxy-auth-type"); } static const char *HttpProxyValidate(xstring_c *p) @@ -130,6 +156,14 @@ const char *OrderValidate(xstring_c *s) return 0; } +static const char *SortByValidate(xstring_c *s) +{ + static const char * const valid_set[]={ + "name", "name-desc", "size", "size-desc", "date", "date-desc", 0 + }; + return SetValidate(*s,valid_set,"mirror:order-by"); +} + #if USE_SSL static const char *AuthArgValidate(xstring_c *s) @@ -137,30 +171,21 @@ const char *AuthArgValidate(xstring_c *s) for(char *i=s->get_non_const(); *i; i++) *i=to_ascii_upper(*i); - if(strcmp(*s,"SSL") - && strcmp(*s,"TLS") - && strcmp(*s,"TLS-P") - && strcmp(*s,"TLS-C")) - return _("ftp:ssl-auth must be one of: SSL, TLS, TLS-P, TLS-C"); - - return 0; + const char *const valid_set[]={ + "SSL", "TLS", "TLS-P", "TLS-C", 0 + }; + return SetValidate(*s,valid_set,"ftp:ssl-auth"); } static const char *ProtValidate(xstring_c *s) { - if(!**s) - return 0; - for(char *i=s->get_non_const(); *i; i++) *i=to_ascii_upper(*i); - if(strcmp(*s,"P") - && strcmp(*s,"C") - && strcmp(*s,"S") - && strcmp(*s,"E")) - return _("must be one of: C, S, E, P, or empty"); - - return 0; + const char *const valid_set[]={ + "C", "S", "E", "P", "", 0 + }; + return SetValidate(*s,valid_set,"ftps:initial-prot"); } #endif @@ -300,6 +325,7 @@ static ResType lftp_vars[] = { {"net:connection-limit", "0", ResMgr::UNumberValidate,0}, {"net:connection-takeover", "yes", ResMgr::BoolValidate,0}, + {"mirror:sort-by", "name", SortByValidate,ResMgr::NoClosure}, {"mirror:order", "*.sfv *.sig *.md5* *.sum * */", 0,ResMgr::NoClosure}, {"mirror:parallel-directories", "yes", ResMgr::BoolValidate,ResMgr::NoClosure}, {"mirror:parallel-transfer-count", "1",ResMgr::UNumberValidate,ResMgr::NoClosure}, diff --git a/src/xmap.cc b/src/xmap.cc index 4bc9a86..2276bce 100644 --- a/src/xmap.cc +++ b/src/xmap.cc @@ -136,6 +136,7 @@ void _xmap::_remove(entry **ep) if(!ep || !*ep) return; entry *e=*ep; + e->key.unset(); *ep=e->next; xfree(e); entry_count--; diff --git a/src/xstring.h b/src/xstring.h index 264baf6..4a284ee 100644 --- a/src/xstring.h +++ b/src/xstring.h @@ -222,7 +222,8 @@ public: static xstring& cat(const char *first,...) ATTRIBUTE_SENTINEL; static xstring& join(const char *sep,int n,...); - void truncate(size_t n=0); + void truncate() { set_length(0); } + void truncate(size_t n); void truncate_at(char c); /* set_length can be used to extend the string, e.g. after modification with get_space+get_non_const. */