bool rpc_manager::incoming(msg const& m, node_id* id) { INVARIANT_CHECK; if (m_destructing) return false; // we only deal with replies, not queries TORRENT_ASSERT(m.message.dict_find_string_value("y") == "r"); // if we don't have the transaction id in our // request list, ignore the packet std::string transaction_id = m.message.dict_find_string_value("t"); std::string::const_iterator i = transaction_id.begin(); int tid = transaction_id.size() != 2 ? -1 : io::read_uint16(i); observer_ptr o; for (transactions_t::iterator i = m_transactions.begin() , end(m_transactions.end()); i != end; ++i) { TORRENT_ASSERT(*i); if ((*i)->transaction_id() != tid) continue; if (m.addr.address() != (*i)->target_addr()) continue; o = *i; m_transactions.erase(i); break; } if (!o) { #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(rpc) << "Reply with invalid transaction id size: " << transaction_id.size() << " from " << m.addr; #endif entry e; incoming_error(e, "invalid transaction id"); m_sock->send_packet(e, m.addr, 0); return false; } ptime now = time_now_hires(); #ifdef TORRENT_DHT_VERBOSE_LOGGING std::ofstream reply_stats("round_trip_ms.log", std::ios::app); reply_stats << m.addr << "\t" << total_milliseconds(now - o->sent()) << std::endl; #endif lazy_entry const* ret_ent = m.message.dict_find_dict("r"); if (ret_ent == 0) { entry e; incoming_error(e, "missing 'r' key"); m_sock->send_packet(e, m.addr, 0); return false; } lazy_entry const* node_id_ent = ret_ent->dict_find_string("id"); if (node_id_ent == 0 || node_id_ent->string_length() != 20) { entry e; incoming_error(e, "missing 'id' key"); m_sock->send_packet(e, m.addr, 0); return false; } lazy_entry const* ext_ip = ret_ent->dict_find_string("ip"); if (ext_ip && ext_ip->string_length() == 4) { // this node claims we use the wrong node-ID! address_v4::bytes_type b; memcpy(&b[0], ext_ip->string_ptr(), 4); if (m_observer) m_observer->set_external_address(address_v4(b) , m.addr.address()); } #if TORRENT_USE_IPV6 else if (ext_ip && ext_ip->string_length() == 16) { // this node claims we use the wrong node-ID! address_v6::bytes_type b; memcpy(&b[0], ext_ip->string_ptr(), 16); if (m_observer) m_observer->set_external_address(address_v6(b) , m.addr.address()); } #endif #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] Reply with transaction id: " << tid << " from " << m.addr; #endif o->reply(m); *id = node_id(node_id_ent->string_ptr()); int rtt = total_milliseconds(now - o->sent()); // we found an observer for this reply, hence the node is not spoofing // add it to the routing table return m_table.node_seen(*id, m.addr, rtt); }
void set_merkle_tree(std::vector<sha1_hash>& h) { TORRENT_ASSERT(h.size() == m_merkle_tree.size() ); m_merkle_tree.swap(h); }
void assign(std::string const& s) { TORRENT_ASSERT(s.size() >= 20); size_t sl = s.size() < size_t(size) ? s.size() : size_t(size); std::memcpy(m_number, s.c_str(), sl); }
file_storage const& orig_files() const { TORRENT_ASSERT(is_loaded()); return m_orig_files ? *m_orig_files : m_files; }
// This function will map a piece index, a byte offset within that piece // and a size (in bytes) into the corresponding files with offsets where // that data for that piece is supposed to be stored. See file_slice. std::vector<file_slice> map_block(int piece, boost::int64_t offset, int size) const { TORRENT_ASSERT(is_loaded()); return m_files.map_block(piece, offset, size); }
void set_bit(int index) { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < size()); buf()[index / 32] |= aux::host_to_network((0x80000000 >> (index & 31))); }
void set_low_priority(file_handle const& f) { // file prio is only supported on vista and up // so load the functions dynamically typedef enum _FILE_INFO_BY_HANDLE_CLASS { FileBasicInfo, FileStandardInfo, FileNameInfo, FileRenameInfo, FileDispositionInfo, FileAllocationInfo, FileEndOfFileInfo, FileStreamInfo, FileCompressionInfo, FileAttributeTagInfo, FileIdBothDirectoryInfo, FileIdBothDirectoryRestartInfo, FileIoPriorityHintInfo, FileRemoteProtocolInfo, MaximumFileInfoByHandleClass } FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS; typedef enum _PRIORITY_HINT { IoPriorityHintVeryLow = 0, IoPriorityHintLow, IoPriorityHintNormal, MaximumIoPriorityHintType } PRIORITY_HINT; typedef struct _FILE_IO_PRIORITY_HINT_INFO { PRIORITY_HINT PriorityHint; } FILE_IO_PRIORITY_HINT_INFO, *PFILE_IO_PRIORITY_HINT_INFO; typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize); static SetFileInformationByHandle_t SetFileInformationByHandle = NULL; static bool failed_kernel_load = false; if (failed_kernel_load) return; if (SetFileInformationByHandle == NULL) { HMODULE kernel32 = LoadLibraryA("kernel32.dll"); if (kernel32 == NULL) { failed_kernel_load = true; return; } SetFileInformationByHandle = (SetFileInformationByHandle_t)GetProcAddress(kernel32, "SetFileInformationByHandle"); if (SetFileInformationByHandle == NULL) { failed_kernel_load = true; return; } } TORRENT_ASSERT(SetFileInformationByHandle); FILE_IO_PRIORITY_HINT_INFO io_hint; io_hint.PriorityHint = IoPriorityHintLow; SetFileInformationByHandle(f->native_handle(), FileIoPriorityHintInfo, &io_hint, sizeof(io_hint)); }
void write_piece(peer_request const& r, disk_buffer_holder& buffer) { TORRENT_ASSERT(false); }
tracker_manager::~tracker_manager() { TORRENT_ASSERT(m_abort); abort_all_requests(true); }
~rootdevice() { TORRENT_ASSERT(magic == 1337); magic = 0; }
void i2p_stream::read_line(error_code const& e, boost::shared_ptr<handler_type> h) { TORRENT_ASSERT(m_magic == 0x1337); #if defined TORRENT_ASIO_DEBUGGING complete_async("i2p_stream::read_line"); #endif if (handle_error(e, h)) return; int read_pos = m_buffer.size(); // look for \n which means end of the response if (m_buffer[read_pos - 1] != '\n') { #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::read_line"); #endif // read another byte from the socket m_buffer.resize(read_pos + 1); async_read(m_sock, asio::buffer(&m_buffer[read_pos], 1) , boost::bind(&i2p_stream::read_line, this, _1, h)); return; } m_buffer[read_pos - 1] = 0; if (m_command == cmd_incoming) { // this is the line containing the destination // of the incoming connection in an accept call m_dest = &m_buffer[0]; (*h)(e); std::vector<char>().swap(m_buffer); return; } error_code invalid_response(i2p_error::parse_failed , get_i2p_category()); // null-terminate the string and parse it m_buffer.push_back(0); char* ptr = &m_buffer[0]; char* next = ptr; char const* expect1 = 0; char const* expect2 = 0; switch (m_state) { case read_hello_response: expect1 = "HELLO"; expect2 = "REPLY"; break; case read_connect_response: case read_accept_response: expect1 = "STREAM"; expect2 = "STATUS"; break; case read_session_create_response: expect1 = "SESSION"; expect2 = "STATUS"; break; case read_name_lookup_response: expect1 = "NAMING"; expect2 = "REPLY"; break; } // fprintf(stderr, "<<< %s\n", &m_buffer[0]); ptr = string_tokenize(next, ' ', &next); if (ptr == 0 || expect1 == 0 || strcmp(expect1, ptr)) { handle_error(invalid_response, h); return; } ptr = string_tokenize(next, ' ', &next); if (ptr == 0 || expect2 == 0 || strcmp(expect2, ptr)) { handle_error(invalid_response, h); return; } int result = 0; // char const* message = 0; // float version = 3.0f; for(;;) { char* name = string_tokenize(next, '=', &next); if (name == 0) break; // fprintf(stderr, "name=\"%s\"\n", name); char* ptr = string_tokenize(next, ' ', &next); if (ptr == 0) { handle_error(invalid_response, h); return; } // fprintf(stderr, "value=\"%s\"\n", ptr); if (strcmp("RESULT", name) == 0) { if (strcmp("OK", ptr) == 0) result = i2p_error::no_error; else if (strcmp("CANT_REACH_PEER", ptr) == 0) result = i2p_error::cant_reach_peer; else if (strcmp("I2P_ERROR", ptr) == 0) result = i2p_error::i2p_error; else if (strcmp("INVALID_KEY", ptr) == 0) result = i2p_error::invalid_key; else if (strcmp("INVALID_ID", ptr) == 0) result = i2p_error::invalid_id; else if (strcmp("TIMEOUT", ptr) == 0) result = i2p_error::timeout; else if (strcmp("KEY_NOT_FOUND", ptr) == 0) result = i2p_error::key_not_found; else if (strcmp("DUPLICATED_ID", ptr) == 0) result = i2p_error::duplicated_id; else result = i2p_error::num_errors; // unknown error } else if (strcmp("MESSAGE", name) == 0) { // message = ptr; } else if (strcmp("VERSION", name) == 0) { // version = float(atof(ptr)); } else if (strcmp("VALUE", name) == 0) { m_name_lookup = ptr; } else if (strcmp("DESTINATION", name) == 0) { m_dest = ptr; } } if (result != i2p_error::no_error) { error_code ec(result, get_i2p_category()); handle_error(ec, h); return; } switch (m_state) { case read_hello_response: switch (m_command) { case cmd_create_session: send_session_create(h); break; case cmd_accept: send_accept(h); break; case cmd_connect: send_connect(h); break; default: (*h)(e); std::vector<char>().swap(m_buffer); } break; case read_connect_response: case read_session_create_response: case read_name_lookup_response: (*h)(e); std::vector<char>().swap(m_buffer); break; case read_accept_response: // the SAM bridge is waiting for an incoming // connection. // wait for one more line containing // the destination of the remote peer m_command = cmd_incoming; m_buffer.resize(1); #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("i2p_stream::read_line"); #endif async_read(m_sock, asio::buffer(m_buffer) , boost::bind(&i2p_stream::read_line, this, _1, h)); break; } return; }
bool rpc_manager::incoming(msg const& m, node_id* id, libtorrent::dht_settings const& settings) { INVARIANT_CHECK; if (m_destructing) return false; // we only deal with replies and errors, not queries TORRENT_ASSERT(m.message.dict_find_string_value("y") == "r" || m.message.dict_find_string_value("y") == "e"); // if we don't have the transaction id in our // request list, ignore the packet std::string transaction_id = m.message.dict_find_string_value("t"); if (transaction_id.empty()) return false; std::string::const_iterator i = transaction_id.begin(); int tid = transaction_id.size() != 2 ? -1 : io::read_uint16(i); observer_ptr o; std::pair<transactions_t::iterator, transactions_t::iterator> range = m_transactions.equal_range(tid); for (transactions_t::iterator i = range.first; i != range.second; ++i) { if (m.addr.address() != i->second->target_addr()) continue; o = i->second; i = m_transactions.erase(i); break; } if (!o) { #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(rpc) << "Reply with unknown transaction id size: " << transaction_id.size() << " from " << m.addr; #endif // this isn't necessarily because the other end is doing // something wrong. This can also happen when we restart // the node, and we prematurely abort all outstanding // requests. Also, this opens up a potential magnification // attack. // entry e; // incoming_error(e, "invalid transaction id"); // m_sock->send_packet(e, m.addr, 0); return false; } ptime now = time_now_hires(); #ifdef TORRENT_DHT_VERBOSE_LOGGING std::ofstream reply_stats("round_trip_ms.log", std::ios::app); reply_stats << m.addr << "\t" << total_milliseconds(now - o->sent()) << std::endl; #endif lazy_entry const* ret_ent = m.message.dict_find_dict("r"); if (ret_ent == 0) { // it may be an error ret_ent = m.message.dict_find_dict("e"); o->timeout(); if (ret_ent == NULL) { entry e; incoming_error(e, "missing 'r' key"); m_sock->send_packet(e, m.addr, 0); } return false; } lazy_entry const* node_id_ent = ret_ent->dict_find_string("id"); if (node_id_ent == 0 || node_id_ent->string_length() != 20) { o->timeout(); entry e; incoming_error(e, "missing 'id' key"); m_sock->send_packet(e, m.addr, 0); return false; } node_id nid = node_id(node_id_ent->string_ptr()); if (settings.enforce_node_id && !verify_id(nid, m.addr.address())) { o->timeout(); entry e; incoming_error(e, "invalid node ID"); m_sock->send_packet(e, m.addr, 0); return false; } #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] Reply with transaction id: " << tid << " from " << m.addr; #endif o->reply(m); *id = nid; int rtt = int(total_milliseconds(now - o->sent())); // we found an observer for this reply, hence the node is not spoofing // add it to the routing table return m_table.node_seen(*id, m.addr, rtt); }
void offset(size_type counter) { TORRENT_ASSERT(counter >= 0); m_total_counter += counter; }
time_duration rpc_manager::tick() { INVARIANT_CHECK; const static int short_timeout = 1; const static int timeout = 8; // look for observers that have timed out if (m_transactions.empty()) return seconds(short_timeout); std::list<observer_ptr> timeouts; time_duration ret = seconds(short_timeout); ptime now = time_now(); #if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS ptime last = min_time(); for (transactions_t::iterator i = m_transactions.begin(); i != m_transactions.end(); ++i) { TORRENT_ASSERT((*i)->sent() >= last); last = (*i)->sent(); } #endif for (transactions_t::iterator i = m_transactions.begin(); i != m_transactions.end();) { observer_ptr o = *i; // if we reach an observer that hasn't timed out // break, because every observer after this one will // also not have timed out yet time_duration diff = now - o->sent(); if (diff < seconds(timeout)) { ret = seconds(timeout) - diff; break; } #ifdef TORRENT_DHT_VERBOSE_LOGGING TORRENT_LOG(rpc) << "[" << o->m_algorithm.get() << "] Timing out transaction id: " << (*i)->transaction_id() << " from " << o->target_ep(); #endif m_transactions.erase(i++); timeouts.push_back(o); } std::for_each(timeouts.begin(), timeouts.end(), boost::bind(&observer::timeout, _1)); timeouts.clear(); for (transactions_t::iterator i = m_transactions.begin(); i != m_transactions.end(); ++i) { observer_ptr o = *i; // if we reach an observer that hasn't timed out // break, because every observer after this one will // also not have timed out yet time_duration diff = now - o->sent(); if (diff < seconds(short_timeout)) { ret = seconds(short_timeout) - diff; break; } // don't call short_timeout() again if we've // already called it once if (o->has_short_timeout()) continue; timeouts.push_back(o); } std::for_each(timeouts.begin(), timeouts.end(), boost::bind(&observer::short_timeout, _1)); return ret; }
char const* ptr(int idx) const { TORRENT_ASSERT(idx >= 0); TORRENT_ASSERT(idx < int(m_storage.size())); return &m_storage[idx]; }
char* disk_buffer_pool::allocate_buffer_impl(mutex::scoped_lock& l , char const*) { TORRENT_ASSERT(m_settings_set); TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(l.locked()); TORRENT_UNUSED(l); char* ret; #if TORRENT_HAVE_MMAP if (m_cache_pool) { if (m_free_list.size() <= (m_max_use - m_low_watermark) / 2 && !m_exceeded_max_size) { m_exceeded_max_size = true; m_trigger_cache_trim(); } if (m_free_list.empty()) return 0; boost::uint64_t slot_index = m_free_list.back(); m_free_list.pop_back(); ret = m_cache_pool + (slot_index * 0x4000); TORRENT_ASSERT(is_disk_buffer(ret, l)); } else #endif { #if defined TORRENT_DISABLE_POOL_ALLOCATOR #if TORRENT_USE_PURGABLE_CONTROL kern_return_t res = vm_allocate( mach_task_self(), reinterpret_cast<vm_address_t*>(&ret), 0x4000, VM_FLAGS_PURGABLE | VM_FLAGS_ANYWHERE); if (res != KERN_SUCCESS) ret = NULL; #else ret = page_aligned_allocator::malloc(m_block_size); #endif // TORRENT_USE_PURGABLE_CONTROL #else if (m_using_pool_allocator) { ret = static_cast<char*>(m_pool.malloc()); int effective_block_size = m_cache_buffer_chunk_size ? m_cache_buffer_chunk_size : (std::max)(m_max_use / 10, 1); m_pool.set_next_size(effective_block_size); } else { ret = page_aligned_allocator::malloc(m_block_size); } #endif if (ret == NULL) { m_exceeded_max_size = true; m_trigger_cache_trim(); return 0; } } #if defined TORRENT_DEBUG TORRENT_ASSERT(m_buffers_in_use.count(ret) == 0); m_buffers_in_use.insert(ret); #endif ++m_in_use; if (m_in_use >= m_low_watermark + (m_max_use - m_low_watermark) / 2 && !m_exceeded_max_size) { m_exceeded_max_size = true; m_trigger_cache_trim(); } #if TORRENT_USE_MLOCK if (m_lock_disk_cache) { #ifdef TORRENT_WINDOWS VirtualLock(ret, m_block_size); #else mlock(ret, m_block_size); #endif } #endif // TORRENT_USE_MLOCK TORRENT_ASSERT(is_disk_buffer(ret, l)); return ret; }
bool get_bit(int index) const { TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < size()); return (buf()[index / 32] & aux::host_to_network((0x80000000 >> (index & 31)))) != 0; }
void disk_buffer_pool::set_settings(aux::session_settings const& sett , error_code& ec) { mutex::scoped_lock l(m_pool_mutex); // 0 cache_buffer_chunk_size means 'automatic' (i.e. // proportional to the total disk cache size) m_cache_buffer_chunk_size = sett.get_int(settings_pack::cache_buffer_chunk_size); m_lock_disk_cache = sett.get_bool(settings_pack::lock_disk_cache); #ifndef TORRENT_DISABLE_POOL_ALLOCATOR m_want_pool_allocator = sett.get_bool(settings_pack::use_disk_cache_pool); // if there are no allocated blocks, it's OK to switch allocator if (m_in_use == 0) m_using_pool_allocator = m_want_pool_allocator; #endif #if TORRENT_HAVE_MMAP // if we've already allocated an mmap, we can't change // anything unless there are no allocations in use if (m_cache_pool && m_in_use > 0) return; #endif // only allow changing size if we're not using mmapped // cache, or if we're just about to turn it off if ( #if TORRENT_HAVE_MMAP m_cache_pool == 0 || #endif sett.get_str(settings_pack::mmap_cache).empty()) { int cache_size = sett.get_int(settings_pack::cache_size); if (cache_size < 0) { boost::uint64_t phys_ram = physical_ram(); if (phys_ram == 0) m_max_use = 1024; else m_max_use = phys_ram / 8 / m_block_size; } else { m_max_use = cache_size; } m_low_watermark = m_max_use - (std::max)(16, sett.get_int(settings_pack::max_queued_disk_bytes) / 0x4000); if (m_low_watermark < 0) m_low_watermark = 0; if (m_in_use >= m_max_use && !m_exceeded_max_size) { m_exceeded_max_size = true; m_trigger_cache_trim(); } } #if TORRENT_USE_ASSERTS m_settings_set = true; #endif #if TORRENT_HAVE_MMAP // #error support resizing the map if (m_cache_pool && sett.get_str(settings_pack::mmap_cache).empty()) { TORRENT_ASSERT(m_in_use == 0); munmap(m_cache_pool, boost::uint64_t(m_max_use) * 0x4000); m_cache_pool = 0; // attempt to make MacOS not flush this to disk, making close() // block for a long time ftruncate(m_cache_fd, 0); close(m_cache_fd); m_cache_fd = -1; std::vector<int>().swap(m_free_list); } else if (m_cache_pool == 0 && !sett.get_str(settings_pack::mmap_cache).empty()) { // O_TRUNC here is because we don't actually care about what's // in the file now, there's no need to ever read that into RAM #ifndef O_EXLOCK #define O_EXLOCK 0 #endif m_cache_fd = open(sett.get_str(settings_pack::mmap_cache).c_str(), O_RDWR | O_CREAT | O_EXLOCK | O_TRUNC, 0700); if (m_cache_fd < 0) { ec.assign(errno, boost::system::generic_category()); } else { #ifndef MAP_NOCACHE #define MAP_NOCACHE 0 #endif ftruncate(m_cache_fd, boost::uint64_t(m_max_use) * 0x4000); m_cache_pool = static_cast<char*>(mmap(0, boost::uint64_t(m_max_use) * 0x4000, PROT_READ | PROT_WRITE , MAP_SHARED | MAP_NOCACHE, m_cache_fd, 0)); if (intptr_t(m_cache_pool) == -1) { ec.assign(errno, boost::system::generic_category()); m_cache_pool = 0; // attempt to make MacOS not flush this to disk, making close() // block for a long time ftruncate(m_cache_fd, 0); close(m_cache_fd); m_cache_fd = -1; } else { TORRENT_ASSERT((size_t(m_cache_pool) & 0xfff) == 0); m_free_list.reserve(m_max_use); for (int i = 0; i < m_max_use; ++i) m_free_list.push_back(i); } } } #endif }
file_handle file_pool::open_file(void* st, std::string const& p , int file_index, file_storage const& fs, int m, error_code& ec) { // potentially used to hold a reference to a file object that's // about to be destructed. If we have such object we assign it to // this member to be destructed after we release the mutex. On some // operating systems (such as OSX) closing a file may take a long // time. We don't want to hold the mutex for that. file_handle defer_destruction; mutex::scoped_lock l(m_mutex); #if TORRENT_USE_ASSERTS // we're not allowed to open a file // from a deleted storage! TORRENT_ASSERT(std::find(m_deleted_storages.begin(), m_deleted_storages.end() , std::make_pair(fs.name(), static_cast<void const*>(&fs))) == m_deleted_storages.end()); #endif TORRENT_ASSERT(st != 0); TORRENT_ASSERT(is_complete(p)); TORRENT_ASSERT((m & file::rw_mask) == file::read_only || (m & file::rw_mask) == file::read_write); file_set::iterator i = m_files.find(std::make_pair(st, file_index)); if (i != m_files.end()) { lru_file_entry& e = i->second; e.last_use = aux::time_now(); if (e.key != st && ((e.mode & file::rw_mask) != file::read_only || (m & file::rw_mask) != file::read_only)) { // this means that another instance of the storage // is using the exact same file. ec = errors::file_collision; return file_handle(); } e.key = st; // if we asked for a file in write mode, // and the cached file is is not opened in // write mode, re-open it if ((((e.mode & file::rw_mask) != file::read_write) && ((m & file::rw_mask) == file::read_write)) || (e.mode & file::random_access) != (m & file::random_access)) { // close the file before we open it with // the new read/write privilages, since windows may // file opening a file twice. However, since there may // be outstanding operations on it, we can't close the // file, we can only delete our reference to it. // if this is the only reference to the file, it will be closed defer_destruction = e.file_ptr; e.file_ptr = boost::make_shared<file>(); std::string full_path = fs.file_path(file_index, p); if (!e.file_ptr->open(full_path, m, ec)) { m_files.erase(i); return file_handle(); } #ifdef TORRENT_WINDOWS if (m_low_prio_io) set_low_priority(e.file_ptr); #endif TORRENT_ASSERT(e.file_ptr->is_open()); e.mode = m; } return e.file_ptr; } lru_file_entry e; e.file_ptr = boost::make_shared<file>(); if (!e.file_ptr) { ec = error_code(ENOMEM, get_posix_category()); return e.file_ptr; } std::string full_path = fs.file_path(file_index, p); if (!e.file_ptr->open(full_path, m, ec)) return file_handle(); #ifdef TORRENT_WINDOWS if (m_low_prio_io) set_low_priority(e.file_ptr); #endif e.mode = m; e.key = st; m_files.insert(std::make_pair(std::make_pair(st, file_index), e)); TORRENT_ASSERT(e.file_ptr->is_open()); file_handle file_ptr = e.file_ptr; // the file is not in our cache if (int(m_files.size()) >= m_size) { // the file cache is at its maximum size, close // the least recently used (lru) file from it remove_oldest(l); } return file_ptr; }
void disk_buffer_pool::free_buffer_impl(char* buf, mutex::scoped_lock& l) { TORRENT_ASSERT(buf); TORRENT_ASSERT(m_magic == 0x1337); TORRENT_ASSERT(m_settings_set); TORRENT_ASSERT(is_disk_buffer(buf, l)); TORRENT_ASSERT(l.locked()); TORRENT_UNUSED(l); #if TORRENT_USE_MLOCK if (m_lock_disk_cache) { #ifdef TORRENT_WINDOWS VirtualUnlock(buf, m_block_size); #else munlock(buf, m_block_size); #endif } #endif #if TORRENT_HAVE_MMAP if (m_cache_pool) { TORRENT_ASSERT(buf >= m_cache_pool); TORRENT_ASSERT(buf < m_cache_pool + boost::uint64_t(m_max_use) * 0x4000); int slot_index = (buf - m_cache_pool) / 0x4000; m_free_list.push_back(slot_index); #if defined MADV_FREE // tell the virtual memory system that we don't actually care // about the data in these pages anymore. If this block was // swapped out to the SSD, it (hopefully) means it won't have // to be read back in once we start writing our new data to it madvise(buf, 0x4000, MADV_FREE); #elif defined MADV_DONTNEED && defined TORRENT_LINUX // rumor has it that MADV_DONTNEED is in fact destructive // on linux (i.e. it won't flush it to disk or re-read from disk) // http://kerneltrap.org/mailarchive/linux-kernel/2007/5/1/84410 madvise(buf, 0x4000, MADV_DONTNEED); #endif } else #endif { #if defined TORRENT_DISABLE_POOL_ALLOCATOR #if TORRENT_USE_PURGABLE_CONTROL vm_deallocate( mach_task_self(), reinterpret_cast<vm_address_t>(buf), 0x4000 ); #else page_aligned_allocator::free(buf); #endif // TORRENT_USE_PURGABLE_CONTROL #else if (m_using_pool_allocator) m_pool.free(buf); else page_aligned_allocator::free(buf); #endif // TORRENT_DISABLE_POOL_ALLOCATOR } #if defined TORRENT_DEBUG std::set<char*>::iterator i = m_buffers_in_use.find(buf); TORRENT_ASSERT(i != m_buffers_in_use.end()); m_buffers_in_use.erase(i); #endif --m_in_use; #ifndef TORRENT_DISABLE_POOL_ALLOCATOR // should we switch which allocator to use? if (m_in_use == 0 && m_want_pool_allocator != m_using_pool_allocator) { m_pool.release_memory(); m_using_pool_allocator = m_want_pool_allocator; } #endif }
int receive_buffer::reserve(std::array<boost::asio::mutable_buffer, 2>& vec, int size) { TORRENT_ASSERT(size > 0); TORRENT_ASSERT(m_recv_pos >= 0); TORRENT_ASSERT(m_packet_size > 0); // normalize() must be called before receiving more data TORRENT_ASSERT(m_recv_start == 0); // this is unintuitive, but we used to use m_recv_pos in this function when // we should have used m_recv_end. perhaps they always happen to be equal TORRENT_ASSERT(m_recv_pos == m_recv_end); int num_bufs = -1; int const regular_buf_size = regular_buffer_size(); if (int(m_recv_buffer.size()) < regular_buf_size) m_recv_buffer.resize(round_up8(regular_buf_size)); if (!m_disk_recv_buffer || regular_buf_size >= m_recv_end + size) { // only receive into regular buffer TORRENT_ASSERT(m_recv_end + size <= int(m_recv_buffer.size())); vec[0] = boost::asio::buffer(&m_recv_buffer[0] + m_recv_end, size); TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) > 0); num_bufs = 1; } else if (m_recv_end >= regular_buf_size) { // only receive into disk buffer TORRENT_ASSERT(m_recv_end - regular_buf_size >= 0); TORRENT_ASSERT(m_recv_end - regular_buf_size + size <= m_disk_recv_buffer_size); vec[0] = boost::asio::buffer(m_disk_recv_buffer.get() + m_recv_end - regular_buf_size, size); TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) > 0); num_bufs = 1; } else { // receive into both regular and disk buffer TORRENT_ASSERT(size + m_recv_end > regular_buf_size); TORRENT_ASSERT(m_recv_end < regular_buf_size); TORRENT_ASSERT(size - regular_buf_size + m_recv_end <= m_disk_recv_buffer_size); vec[0] = boost::asio::buffer(&m_recv_buffer[0] + m_recv_end , regular_buf_size - m_recv_end); vec[1] = boost::asio::buffer(m_disk_recv_buffer.get() , size - regular_buf_size + m_recv_end); TORRENT_ASSERT(boost::asio::buffer_size(vec[0]) + boost::asio::buffer_size(vec[1])> 0); num_bufs = 2; } return num_bufs; }
has_index(int i): index(boost::uint32_t(i)) { TORRENT_ASSERT(i >= 0); }
// Renames a the file with the specified index to the new name. The new // filename is reflected by the ``file_storage`` returned by ``files()`` // but not by the one returned by ``orig_files()``. // // If you want to rename the base name of the torrent (for a multifile // torrent), you can copy the ``file_storage`` (see files() and // orig_files() ), change the name, and then use `remap_files()`_. // // The ``new_filename`` can both be a relative path, in which case the // file name is relative to the ``save_path`` of the torrent. If the // ``new_filename`` is an absolute path (i.e. ``is_complete(new_filename) // == true``), then the file is detached from the ``save_path`` of the // torrent. In this case the file is not moved when move_storage() is // invoked. void rename_file(int index, std::string const& new_filename) { TORRENT_ASSERT(is_loaded()); copy_on_write(); m_files.rename_file(index, new_filename); }
void stat_cache::set_dirty(int const i) { TORRENT_ASSERT(i >= 0); if (i >= int(m_stat_cache.size())) return; m_stat_cache[i].file_size = not_in_cache; }
// This function will map a range in a specific file into a range in the // torrent. The ``file_offset`` parameter is the offset in the file, // given in bytes, where 0 is the start of the file. See peer_request. // // The input range is assumed to be valid within the torrent. // ``file_offset`` + ``size`` is not allowed to be greater than the file // size. ``file_index`` must refer to a valid file, i.e. it cannot be >= // ``num_files()``. peer_request map_file(int file, boost::int64_t offset, int size) const { TORRENT_ASSERT(is_loaded()); return m_files.map_file(file, offset, size); }
void bandwidth_manager::update_quotas(time_duration const& dt) { if (m_abort) return; if (m_queue.empty()) return; INVARIANT_CHECK; boost::int64_t dt_milliseconds = total_milliseconds(dt); if (dt_milliseconds > 3000) dt_milliseconds = 3000; // for each bandwidth channel, call update_quota(dt) std::vector<bandwidth_channel*> channels; queue_t tm; for (queue_t::iterator i = m_queue.begin(); i != m_queue.end();) { if (i->peer->is_disconnecting()) { m_queued_bytes -= i->request_size - i->assigned; // return all assigned quota to all the // bandwidth channels this peer belongs to for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) { bandwidth_channel* bwc = i->channel[j]; bwc->return_quota(i->assigned); } i->assigned = 0; tm.push_back(*i); i = m_queue.erase(i); continue; } for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) { bandwidth_channel* bwc = i->channel[j]; bwc->tmp = 0; } ++i; } for (queue_t::iterator i = m_queue.begin() , end(m_queue.end()); i != end; ++i) { for (int j = 0; j < bw_request::max_bandwidth_channels && i->channel[j]; ++j) { bandwidth_channel* bwc = i->channel[j]; if (bwc->tmp == 0) channels.push_back(bwc); TORRENT_ASSERT(INT_MAX - bwc->tmp > i->priority); bwc->tmp += i->priority; } } for (std::vector<bandwidth_channel*>::iterator i = channels.begin() , end(channels.end()); i != end; ++i) { (*i)->update_quota(int(dt_milliseconds)); } for (queue_t::iterator i = m_queue.begin(); i != m_queue.end();) { int a = i->assign_bandwidth(); if (i->assigned == i->request_size || (i->ttl <= 0 && i->assigned > 0)) { a += i->request_size - i->assigned; TORRENT_ASSERT(i->assigned <= i->request_size); tm.push_back(*i); i = m_queue.erase(i); } else { ++i; } m_queued_bytes -= a; } while (!tm.empty()) { bw_request& bwr = tm.back(); bwr.peer->assign_bandwidth(m_channel, bwr.assigned); tm.pop_back(); } }
explicit sha1_hash(std::string const& s) { TORRENT_ASSERT(s.size() >= 20); size_t sl = s.size() < size_t(size) ? s.size() : size_t(size); std::memcpy(m_number, s.c_str(), sl); }
void torrent_history::handle_alert(alert const* a) { add_torrent_alert const* ta = alert_cast<add_torrent_alert>(a); torrent_removed_alert const* td = alert_cast<torrent_removed_alert>(a); state_update_alert const* su = alert_cast<state_update_alert>(a); torrent_update_alert const* tu = alert_cast<torrent_update_alert>(a); if (tu) { mutex::scoped_lock l(m_mutex); // first remove the old hash m_removed.push_front(std::make_pair(m_frame + 1, tu->old_ih)); torrent_history_entry st; st.status.info_hash = tu->old_ih; queue_t::right_iterator it = m_queue.right.find(st); if (it == m_queue.right.end()) return; st = it->first; m_queue.right.erase(st); // then add the torrent under the new inf-hash st.status.info_hash = tu->new_ih; m_queue.left.push_front(std::make_pair(m_frame + 1, st)); // weed out torrents that were removed a long time ago while (m_removed.size() > 1000 && m_removed.back().first < m_frame - 10) m_removed.pop_back(); m_deferred_frame_count = true; } else if (ta) { torrent_status st = ta->handle.status(); TORRENT_ASSERT(st.info_hash == st.handle.info_hash()); TORRENT_ASSERT(st.handle == ta->handle); mutex::scoped_lock l(m_mutex); m_queue.left.push_front(std::make_pair(m_frame + 1, torrent_history_entry(st, m_frame + 1))); m_deferred_frame_count = true; } else if (td) { mutex::scoped_lock l(m_mutex); m_removed.push_front(std::make_pair(m_frame + 1, td->info_hash)); torrent_history_entry st; st.status.info_hash = td->info_hash; m_queue.right.erase(st); // weed out torrents that were removed a long time ago while (m_removed.size() > 1000 && m_removed.back().first < m_frame - 10) m_removed.pop_back(); m_deferred_frame_count = true; } else if (su) { mutex::scoped_lock l(m_mutex); ++m_frame; m_deferred_frame_count = false; std::vector<torrent_status> const& st = su->status; for (std::vector<torrent_status>::const_iterator i = st.begin() , end(st.end()); i != end; ++i) { torrent_history_entry e; e.status.info_hash = i->info_hash; queue_t::right_iterator it = m_queue.right.find(e); if (it == m_queue.right.end()) continue; const_cast<torrent_history_entry&>(it->first).update_status(*i, m_frame); m_queue.right.replace_data(it, m_frame); // bump this torrent to the beginning of the list m_queue.left.relocate(m_queue.left.begin(), m_queue.project_left(it)); } /* printf("===== frame: %d =====\n", m_frame); for (queue_t::left_iterator i = m_queue.left.begin() , end(m_queue.left.end()); i != end; ++i) { i->second.debug_print(m_frame); // printf("%3d: (%s) %s\n", i->first, i->second.error.c_str(), i->second.name.c_str()); } */ } }
// accessors for specific bytes boost::uint8_t& operator[](int i) { TORRENT_ASSERT(i >= 0 && i < size); return reinterpret_cast<boost::uint8_t*>(m_number)[i]; }
void dec_num_connecting() { TORRENT_ASSERT(m_num_connecting > 0); --m_num_connecting; }