void join() { if (get_id() == GetCurrentThreadId()) throw system_error(EDEADLK, generic_category()); if (mHandle == _STD_THREAD_INVALID_HANDLE) throw system_error(ESRCH, generic_category()); if (!joinable()) throw system_error(EINVAL, generic_category()); WaitForSingleObject(mHandle, INFINITE); CloseHandle(mHandle); mHandle = _STD_THREAD_INVALID_HANDLE; mThreadId.clear(); }
const error_category& posix_category() { #ifdef LLVM_ON_WIN32 return generic_category(); #else return system_category(); #endif }
int posix_storage::readv(session_settings const& , span<iovec_t const> bufs , piece_index_t const piece, int const offset , storage_error& error) { return readwritev(files(), bufs, piece, offset, error , [this](file_index_t const file_index , std::int64_t const file_offset , span<iovec_t const> vec, storage_error& ec) { if (files().pad_file_at(file_index)) { // reading from a pad file yields zeroes clear_bufs(vec); return bufs_size(vec); } FILE* const f = open_file(file_index, open_mode::read_only , file_offset, ec); if (ec.ec) return -1; // set this unconditionally in case the upper layer would like to treat // short reads as errors ec.operation = operation_t::file_read; int ret = 0; for (auto buf : vec) { int const r = static_cast<int>(fread(buf.data(), 1 , static_cast<std::size_t>(buf.size()), f)); if (r == 0) { ec.ec.assign(errno, generic_category()); break; } ret += r; // the file may be shorter than we think if (r < buf.size()) break; } fclose(f); // we either get an error or 0 or more bytes read TORRENT_ASSERT(ec.ec || ret > 0); TORRENT_ASSERT(ret <= bufs_size(vec)); if (ec.ec) { ec.file(file_index); return -1; } return ret; }); }
address bind_to_device(io_service& ios, Socket& sock , bool ipv4, char const* device_name, int port, error_code& ec) { boost::asio::ip::tcp::endpoint bind_ep(address_v4::any(), port); address ip = address::from_string(device_name, ec); if (!ec) { bind_ep.address(ip); // it appears to be an IP. Just bind to that address sock.bind(bind_ep, ec); return bind_ep.address(); } ec.clear(); #ifdef SO_BINDTODEVICE // try to use SO_BINDTODEVICE here, if that exists. If it fails, // fall back to the mechanism we have below sock.set_option(bind_to_device_opt(device_name), ec); if (ec) #endif { ec.clear(); // TODO: 2 this could be done more efficiently by just looking up // the interface with the given name, maybe even with if_nametoindex() std::vector<ip_interface> ifs = enum_net_interfaces(ios, ec); if (ec) return bind_ep.address(); bool found = false; for (int i = 0; i < int(ifs.size()); ++i) { // we're looking for a specific interface, and its address // (which must be of the same family as the address we're // connecting to) if (strcmp(ifs[i].name, device_name) != 0) continue; if (ifs[i].interface_address.is_v4() != ipv4) continue; bind_ep.address(ifs[i].interface_address); found = true; break; } if (!found) { ec = error_code(boost::system::errc::no_such_device, generic_category()); return bind_ep.address(); } } sock.bind(bind_ep, ec); return bind_ep.address(); }
int posix_storage::writev(session_settings const& , span<iovec_t const> bufs , piece_index_t const piece, int const offset , storage_error& error) { return readwritev(files(), bufs, piece, offset, error , [this](file_index_t const file_index , std::int64_t const file_offset , span<iovec_t const> vec, storage_error& ec) { if (files().pad_file_at(file_index)) { // writing to a pad-file is a no-op return bufs_size(vec); } FILE* const f = open_file(file_index, open_mode::write , file_offset, ec); if (ec.ec) return -1; // set this unconditionally in case the upper layer would like to treat // short reads as errors ec.operation = operation_t::file_write; int ret = 0; for (auto buf : vec) { auto const r = static_cast<int>(fwrite(buf.data(), 1 , static_cast<std::size_t>(buf.size()), f)); if (r != buf.size()) { ec.ec.assign(errno, generic_category()); break; } ret += r; } fclose(f); // invalidate our stat cache for this file, since // we're writing to it m_stat_cache.set_dirty(file_index); if (ec) { ec.file(file_index); return -1; } return ret; }); }
// this has to be thread safe and atomic. i.e. on posix systems it has to be // turned into a series of pread() calls std::int64_t file::readv(std::int64_t file_offset, span<iovec_t const> bufs , error_code& ec, open_mode_t flags) { if (m_file_handle == INVALID_HANDLE_VALUE) { #ifdef TORRENT_WINDOWS ec = error_code(ERROR_INVALID_HANDLE, system_category()); #else ec = error_code(boost::system::errc::bad_file_descriptor, generic_category()); #endif return -1; } TORRENT_ASSERT((m_open_mode & open_mode::rw_mask) == open_mode::read_only || (m_open_mode & open_mode::rw_mask) == open_mode::read_write); TORRENT_ASSERT(!bufs.empty()); TORRENT_ASSERT(is_open()); #if TORRENT_USE_PREADV TORRENT_UNUSED(flags); std::int64_t ret = iov(&::preadv, native_handle(), file_offset, bufs, ec); #else // there's no point in coalescing single buffer writes if (bufs.size() == 1) { flags &= ~open_mode::coalesce_buffers; } iovec_t tmp; span<iovec_t const> tmp_bufs = bufs; if (flags & open_mode::coalesce_buffers) { if (!coalesce_read_buffers(tmp_bufs, tmp)) // ok, that failed, don't coalesce this read flags &= ~open_mode::coalesce_buffers; } #if TORRENT_USE_PREAD std::int64_t ret = iov(&::pread, native_handle(), file_offset, tmp_bufs, ec); #else std::int64_t ret = iov(&::read, native_handle(), file_offset, tmp_bufs, ec); #endif if (flags & open_mode::coalesce_buffers) coalesce_read_buffers_end(bufs , tmp.data(), !ec); #endif return ret; }
void clear() { m_val = 0; m_cat = &generic_category(); }
namespace system { class error_code; class error_condition; // "Concept" helpers ---------------------------------------------------// template< class T > struct is_error_code_enum { static const bool value = false; }; template< class T > struct is_error_condition_enum { static const bool value = false; }; // generic error_conditions --------------------------------------------// namespace errc { enum errc_t { success = 0, address_family_not_supported = EAFNOSUPPORT, address_in_use = EADDRINUSE, address_not_available = EADDRNOTAVAIL, already_connected = EISCONN, argument_list_too_long = E2BIG, argument_out_of_domain = EDOM, bad_address = EFAULT, bad_file_descriptor = EBADF, bad_message = EBADMSG, broken_pipe = EPIPE, connection_aborted = ECONNABORTED, connection_already_in_progress = EALREADY, connection_refused = ECONNREFUSED, connection_reset = ECONNRESET, cross_device_link = EXDEV, destination_address_required = EDESTADDRREQ, device_or_resource_busy = EBUSY, directory_not_empty = ENOTEMPTY, executable_format_error = ENOEXEC, file_exists = EEXIST, file_too_large = EFBIG, filename_too_long = ENAMETOOLONG, function_not_supported = ENOSYS, host_unreachable = EHOSTUNREACH, identifier_removed = EIDRM, illegal_byte_sequence = EILSEQ, inappropriate_io_control_operation = ENOTTY, interrupted = EINTR, invalid_argument = EINVAL, invalid_seek = ESPIPE, io_error = EIO, is_a_directory = EISDIR, message_size = EMSGSIZE, network_down = ENETDOWN, network_reset = ENETRESET, network_unreachable = ENETUNREACH, no_buffer_space = ENOBUFS, no_child_process = ECHILD, no_link = ENOLINK, no_lock_available = ENOLCK, no_message_available = ENODATA, no_message = ENOMSG, no_protocol_option = ENOPROTOOPT, no_space_on_device = ENOSPC, no_stream_resources = ENOSR, no_such_device_or_address = ENXIO, no_such_device = ENODEV, no_such_file_or_directory = ENOENT, no_such_process = ESRCH, not_a_directory = ENOTDIR, not_a_socket = ENOTSOCK, not_a_stream = ENOSTR, not_connected = ENOTCONN, not_enough_memory = ENOMEM, not_supported = ENOTSUP, operation_canceled = ECANCELED, operation_in_progress = EINPROGRESS, operation_not_permitted = EPERM, operation_not_supported = EOPNOTSUPP, operation_would_block = EWOULDBLOCK, owner_dead = EOWNERDEAD, permission_denied = EACCES, protocol_error = EPROTO, protocol_not_supported = EPROTONOSUPPORT, read_only_file_system = EROFS, resource_deadlock_would_occur = EDEADLK, resource_unavailable_try_again = EAGAIN, result_out_of_range = ERANGE, state_not_recoverable = ENOTRECOVERABLE, stream_timeout = ETIME, text_file_busy = ETXTBSY, timed_out = ETIMEDOUT, too_many_files_open_in_system = ENFILE, too_many_files_open = EMFILE, too_many_links = EMLINK, too_many_symbolic_link_levels = ELOOP, value_too_large = EOVERFLOW, wrong_protocol_type = EPROTOTYPE }; } // namespace errc # ifndef BOOST_SYSTEM_NO_DEPRECATED namespace posix = errc; namespace posix_error = errc; # endif template<> struct is_error_condition_enum<errc::errc_t> { static const bool value = true; }; // ----------------------------------------------------------------------// // Operating system specific interfaces --------------------------------// // The interface is divided into general and system-specific portions to // meet these requirements: // // * Code calling an operating system API can create an error_code with // a single category (system_category), even for POSIX-like operating // systems that return some POSIX errno values and some native errno // values. This code should not have to pay the cost of distinguishing // between categories, since it is not yet known if that is needed. // // * Users wishing to write system-specific code should be given enums for // at least the common error cases. // // * System specific code should fail at compile time if moved to another // operating system. // The system specific portions of the interface are located in headers // with names reflecting the operating system. For example, // // <boost/system/cygwin_error.hpp> // <boost/system/linux_error.hpp> // <boost/system/windows_error.hpp> // // These headers are effectively empty for compiles on operating systems // where they are not applicable. // ----------------------------------------------------------------------// // class error_category ------------------------------------------------// class error_category : public noncopyable { public: virtual ~error_category(){} virtual const char * name() const = 0; virtual std::string message( int ev ) const = 0; virtual error_condition default_error_condition( int ev ) const; virtual bool equivalent( int code, const error_condition & condition ) const; virtual bool equivalent( const error_code & code, int condition ) const; bool operator==(const error_category & rhs) const { return this == &rhs; } bool operator!=(const error_category & rhs) const { return this != &rhs; } bool operator<( const error_category & rhs ) const { return std::less<const error_category*>()( this, &rhs ); } }; // predefined error categories -----------------------------------------// BOOST_SYSTEM_DECL const error_category & system_category(); BOOST_SYSTEM_DECL const error_category & generic_category(); // deprecated synonyms --------------------------------------------------// # ifndef BOOST_SYSTEM_NO_DEPRECATED inline const error_category & get_system_category() { return system_category(); } inline const error_category & get_generic_category() { return generic_category(); } inline const error_category & get_posix_category() { return generic_category(); } static const error_category & posix_category = generic_category(); static const error_category & errno_ecat = generic_category(); static const error_category & native_ecat = system_category(); # endif // class error_condition -----------------------------------------------// // error_conditions are portable, error_codes are system or library specific class error_condition { public: // constructors: error_condition() : m_val(0), m_cat(&generic_category()) {} error_condition( int val, const error_category & cat ) : m_val(val), m_cat(&cat) {} template <class ErrorConditionEnum> error_condition(ErrorConditionEnum e, typename hexerboost::enable_if<is_error_condition_enum<ErrorConditionEnum> >::type* = 0) { *this = make_error_condition(e); } // modifiers: void assign( int val, const error_category & cat ) { m_val = val; m_cat = &cat; } template<typename ErrorConditionEnum> typename hexerboost::enable_if<is_error_condition_enum<ErrorConditionEnum>, error_condition>::type & operator=( ErrorConditionEnum val ) { *this = make_error_condition(val); return *this; } void clear() { m_val = 0; m_cat = &generic_category(); } // observers: int value() const { return m_val; } const error_category & category() const { return *m_cat; } std::string message() const { return m_cat->message(value()); } typedef void (*unspecified_bool_type)(); static void unspecified_bool_true() {} operator unspecified_bool_type() const // true if error { return m_val == 0 ? 0 : unspecified_bool_true; } bool operator!() const // true if no error { return m_val == 0; } // relationals: // the more symmetrical non-member syntax allows enum // conversions work for both rhs and lhs. inline friend bool operator==( const error_condition & lhs, const error_condition & rhs ) { return lhs.m_cat == rhs.m_cat && lhs.m_val == rhs.m_val; } inline friend bool operator<( const error_condition & lhs, const error_condition & rhs ) // the more symmetrical non-member syntax allows enum // conversions work for both rhs and lhs. { return lhs.m_cat < rhs.m_cat || (lhs.m_cat == rhs.m_cat && lhs.m_val < rhs.m_val); } private: int m_val; const error_category * m_cat; }; // class error_code ----------------------------------------------------// // We want error_code to be a value type that can be copied without slicing // and without requiring heap allocation, but we also want it to have // polymorphic behavior based on the error category. This is achieved by // abstract base class error_category supplying the polymorphic behavior, // and error_code containing a pointer to an object of a type derived // from error_category. class error_code { public: // constructors: error_code() : m_val(0), m_cat(&system_category()) {} error_code( int val, const error_category & cat ) : m_val(val), m_cat(&cat) {} template <class ErrorCodeEnum> error_code(ErrorCodeEnum e, typename hexerboost::enable_if<is_error_code_enum<ErrorCodeEnum> >::type* = 0) { *this = make_error_code(e); } // modifiers: void assign( int val, const error_category & cat ) { m_val = val; m_cat = &cat; } template<typename ErrorCodeEnum> typename hexerboost::enable_if<is_error_code_enum<ErrorCodeEnum>, error_code>::type & operator=( ErrorCodeEnum val ) { *this = make_error_code(val); return *this; } void clear() { m_val = 0; m_cat = &system_category(); } // observers: int value() const { return m_val; } const error_category & category() const { return *m_cat; } error_condition default_error_condition() const { return m_cat->default_error_condition(value()); } std::string message() const { return m_cat->message(value()); } typedef void (*unspecified_bool_type)(); static void unspecified_bool_true() {} operator unspecified_bool_type() const // true if error { return m_val == 0 ? 0 : unspecified_bool_true; } bool operator!() const // true if no error { return m_val == 0; } // relationals: inline friend bool operator==( const error_code & lhs, const error_code & rhs ) // the more symmetrical non-member syntax allows enum // conversions work for both rhs and lhs. { return lhs.m_cat == rhs.m_cat && lhs.m_val == rhs.m_val; } inline friend bool operator<( const error_code & lhs, const error_code & rhs ) // the more symmetrical non-member syntax allows enum // conversions work for both rhs and lhs. { return lhs.m_cat < rhs.m_cat || (lhs.m_cat == rhs.m_cat && lhs.m_val < rhs.m_val); } private: int m_val; const error_category * m_cat; }; // predefined error_code object used as "throw on error" tag # ifndef BOOST_SYSTEM_NO_DEPRECATED BOOST_SYSTEM_DECL extern error_code throws; # endif // Moving from a "throws" object to a "throws" function without breaking // existing code is a bit of a problem. The workaround is to place the // "throws" function in namespace hexerboost rather than namespace hexerboost::system. } // namespace system
inline const error_category & get_posix_category() { return generic_category(); }
// constructors: error_condition() : m_val(0), m_cat(&generic_category()) {}
address bind_socket_to_device(io_service& ios, Socket& sock , boost::asio::ip::tcp const& protocol , char const* device_name, int port, error_code& ec) { tcp::endpoint bind_ep(address_v4::any(), std::uint16_t(port)); address ip = make_address(device_name, ec); if (!ec) { #if TORRENT_USE_IPV6 // this is to cover the case where "0.0.0.0" is considered any IPv4 or // IPv6 address. If we're asking to be bound to an IPv6 address and // providing 0.0.0.0 as the device, turn it into "::" if (ip == address_v4::any() && protocol == boost::asio::ip::tcp::v6()) ip = address_v6::any(); #endif bind_ep.address(ip); // it appears to be an IP. Just bind to that address sock.bind(bind_ep, ec); return bind_ep.address(); } ec.clear(); #if TORRENT_HAS_BINDTODEVICE // try to use SO_BINDTODEVICE here, if that exists. If it fails, // fall back to the mechanism we have below sock.set_option(aux::bind_to_device(device_name), ec); if (ec) #endif { ec.clear(); // TODO: 2 this could be done more efficiently by just looking up // the interface with the given name, maybe even with if_nametoindex() std::vector<ip_interface> ifs = enum_net_interfaces(ios, ec); if (ec) return bind_ep.address(); bool found = false; for (auto const& iface : ifs) { // we're looking for a specific interface, and its address // (which must be of the same family as the address we're // connecting to) if (std::strcmp(iface.name, device_name) != 0) continue; if (iface.interface_address.is_v4() != (protocol == boost::asio::ip::tcp::v4())) continue; bind_ep.address(iface.interface_address); found = true; break; } if (!found) { ec = error_code(boost::system::errc::no_such_device, generic_category()); return bind_ep.address(); } } sock.bind(bind_ep, ec); return bind_ep.address(); }
void __throw_system_error(int c) { throw system_error(error_code(c, generic_category())); }
file_handle file_pool::open_file(storage_index_t st, std::string const& p , file_index_t const 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 std::mutex. On some // operating systems (such as OSX) closing a file may take a long // time. We don't want to hold the std::mutex for that. file_handle defer_destruction; std::unique_lock<std::mutex> 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(is_complete(p)); TORRENT_ASSERT((m & file::rw_mask) == file::read_only || (m & file::rw_mask) == file::read_write); auto const 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 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)) { file_handle new_file = std::make_shared<file>(); std::string full_path = fs.file_path(file_index, p); if (!new_file->open(full_path, m, ec)) return file_handle(); #ifdef TORRENT_WINDOWS if (m_low_prio_io) set_low_priority(new_file); #endif TORRENT_ASSERT(new_file->is_open()); defer_destruction = std::move(e.file_ptr); e.file_ptr = std::move(new_file); e.mode = m; } return e.file_ptr; } lru_file_entry e; e.file_ptr = std::make_shared<file>(); if (!e.file_ptr) { ec = error_code(boost::system::errc::not_enough_memory, generic_category()); return file_handle(); } 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; file_handle file_ptr = e.file_ptr; m_files.insert(std::make_pair(std::make_pair(st, file_index), e)); TORRENT_ASSERT(file_ptr->is_open()); 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; }
// This has to be thread safe, i.e. atomic. // that means, on posix this has to be turned into a series of // pwrite() calls std::int64_t file::writev(std::int64_t file_offset, span<iovec_t const> bufs , error_code& ec, open_mode_t flags) { if (m_file_handle == INVALID_HANDLE_VALUE) { #ifdef TORRENT_WINDOWS ec = error_code(ERROR_INVALID_HANDLE, system_category()); #else ec = error_code(boost::system::errc::bad_file_descriptor, generic_category()); #endif return -1; } TORRENT_ASSERT((m_open_mode & open_mode::rw_mask) == open_mode::write_only || (m_open_mode & open_mode::rw_mask) == open_mode::read_write); TORRENT_ASSERT(!bufs.empty()); TORRENT_ASSERT(is_open()); ec.clear(); #if TORRENT_USE_PREADV TORRENT_UNUSED(flags); std::int64_t ret = iov(&::pwritev, native_handle(), file_offset, bufs, ec); #else // there's no point in coalescing single buffer writes if (bufs.size() == 1) { flags &= ~open_mode::coalesce_buffers; } iovec_t tmp; if (flags & open_mode::coalesce_buffers) { if (!coalesce_write_buffers(bufs, tmp)) // ok, that failed, don't coalesce writes flags &= ~open_mode::coalesce_buffers; } #if TORRENT_USE_PREAD std::int64_t ret = iov(&::pwrite, native_handle(), file_offset, bufs, ec); #else std::int64_t ret = iov(&::write, native_handle(), file_offset, bufs, ec); #endif if (flags & open_mode::coalesce_buffers) delete[] tmp.data(); #endif #if TORRENT_USE_FDATASYNC \ && !defined F_NOCACHE && \ !defined DIRECTIO_ON if (m_open_mode & open_mode::no_cache) { if (::fdatasync(native_handle()) != 0 && errno != EINVAL && errno != ENOSYS) { ec.assign(errno, system_category()); } } #endif return ret; }
// explicit conversion: inline error_code make_error_code( errc_t e ) { return error_code( e, generic_category() ); }
void do_visit( std::size_t N, std::size_t blockSize, float loadFactor) { using key_type = std::uint32_t; error_code ec; test_store ts{sizeof(key_type), blockSize, loadFactor}; // File not present visit(ts.dp, [&](void const* key, std::size_t keySize, void const* data, std::size_t dataSize, error_code& ec) { }, no_progress{}, ec); if(! BEAST_EXPECTS(ec == errc::no_such_file_or_directory, ec.message())) return; ec = {}; ts.create(ec); if(! BEAST_EXPECTS(! ec, ec.message())) return; ts.open(ec); if(! BEAST_EXPECTS(! ec, ec.message())) return; std::unordered_map<key_type, std::size_t> map; // Insert for(std::size_t i = 0; i < N; ++i) { auto const item = ts[i]; key_type const k = item.key[0] + (static_cast<key_type>(item.key[1]) << 8) + (static_cast<key_type>(item.key[2]) << 16) + (static_cast<key_type>(item.key[3]) << 24); map[k] = i; ts.db.insert(item.key, item.data, item.size, ec); if(! BEAST_EXPECTS(! ec, ec.message())) return; } ts.close(ec); if(! BEAST_EXPECTS(! ec, ec.message())) return; // Visit visit(ts.dp, [&](void const* key, std::size_t keySize, void const* data, std::size_t dataSize, error_code& ec) { auto const fail = [&ec] { ec = error_code{ errc::invalid_argument, generic_category()}; }; if(! BEAST_EXPECT(keySize == sizeof(key_type))) return fail(); auto const p = reinterpret_cast<std::uint8_t const*>(key); key_type const k = p[0] + (static_cast<key_type>(p[1]) << 8) + (static_cast<key_type>(p[2]) << 16) + (static_cast<key_type>(p[3]) << 24); auto const it = map.find(k); if(it == map.end()) return fail(); auto const item = ts[it->second]; if(! BEAST_EXPECT(dataSize == item.size)) return fail(); auto const result = std::memcmp(data, item.data, item.size); if(result != 0) return fail(); }, no_progress{}, ec); if(! BEAST_EXPECTS(! ec, ec.message())) return; }
FILE* posix_storage::open_file(file_index_t idx, open_mode_t const mode , std::int64_t const offset, storage_error& ec) { std::string const fn = files().file_path(idx, m_save_path); char const* mode_str = (mode & open_mode::write) ? "rb+" : "rb"; FILE* f = fopen(fn.c_str(), mode_str); if (f == nullptr) { ec.ec.assign(errno, generic_category()); // if we fail to open a file for writing, and the error is ENOENT, // it is likely because the directory we're creating the file in // does not exist. Create the directory and try again. if ((mode & open_mode::write) && ec.ec == boost::system::errc::no_such_file_or_directory) { // this means the directory the file is in doesn't exist. // so create it ec.ec.clear(); create_directories(parent_path(fn), ec.ec); if (ec.ec) { ec.file(idx); ec.operation = operation_t::mkdir; return nullptr; } // now that we've created the directories, try again // and make sure we create the file this time ("r+") opens for // reading and writing, but doesn't create the file. "w+" creates // the file and truncates it f = fopen(fn.c_str(), "wb+"); if (f == nullptr) { ec.ec.assign(errno, generic_category()); ec.file(idx); ec.operation = operation_t::file_open; return nullptr; } } else { ec.file(idx); ec.operation = operation_t::file_open; return nullptr; } } #ifdef TORRENT_WINDOWS #define fseek _fseeki64 #endif if (offset != 0) { if (fseek(f, offset, SEEK_SET) != 0) { ec.ec.assign(errno, generic_category()); ec.file(idx); ec.operation = operation_t::file_seek; fclose(f); return nullptr; } } return f; }
// implicit conversion: inline error_condition make_error_condition( errc_t e ) { return error_condition( e, generic_category() ); }
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 std::mutex. On some // operating systems (such as OSX) closing a file may take a long // time. We don't want to hold the std::mutex for that. file_handle defer_destruction; std::unique_lock<std::mutex> 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 privileges, 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(boost::system::errc::not_enough_memory, generic_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; }