// (C) Copyright Craig Henderson 2002 'boost/memmap.hpp' from sandbox // (C) Copyright Jonathan Turkanis 2004. // (C) Copyright Jonathan Graehl 2004. // (C) Copyright Jorge Lodos 2008. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) // Define BOOST_IOSTREAMS_SOURCE so that // knows that we are building the library (possibly exporting code), rather // than using it (possibly importing code). #define BOOST_IOSTREAMS_SOURCE #include #include #include #include #include #include #include #ifdef BOOST_IOSTREAMS_WINDOWS # define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers # include #else # include # include # include // mmap, munmap. # include # include // struct stat. # include // sysconf. #endif namespace boost { namespace iostreams { namespace detail { // Class containing the platform-sepecific implementation // Invariant: The members params_, data_, size_, handle_ (and mapped_handle_ // on Windows) either // - all have default values (or INVALID_HANDLE_VALUE for // Windows handles), or // - all have values reflecting a successful mapping. // In the first case, error_ may be true, reflecting a recent unsuccessful // open or close attempt; in the second case, error_ is always false. class mapped_file_impl { public: typedef mapped_file_source::size_type size_type; typedef mapped_file_source::param_type param_type; typedef mapped_file_source::mapmode mapmode; BOOST_STATIC_CONSTANT( size_type, max_length = mapped_file_source::max_length); mapped_file_impl(); ~mapped_file_impl(); void open(param_type p); bool is_open() const { return data_ != 0; } void close(); bool error() const { return error_; } mapmode flags() const { return params_.flags; } std::size_t size() const { return size_; } char* data() const { return data_; } void resize(stream_offset new_size); static int alignment(); private: void open_file(param_type p); void try_map_file(param_type p); void map_file(param_type& p); bool unmap_file(); void clear(bool error); void cleanup_and_throw(const char* msg); param_type params_; char* data_; stream_offset size_; file_handle handle_; #ifdef BOOST_IOSTREAMS_WINDOWS file_handle mapped_handle_; #endif bool error_; }; mapped_file_impl::mapped_file_impl() { clear(false); } mapped_file_impl::~mapped_file_impl() { try { close(); } catch (...) { } } void mapped_file_impl::open(param_type p) { if (is_open()) boost::throw_exception(BOOST_IOSTREAMS_FAILURE("file already open")); p.normalize(); open_file(p); map_file(p); // May modify p.hint params_ = p; } void mapped_file_impl::close() { if (data_ == 0) return; bool error = false; error = !unmap_file() || error; error = #ifdef BOOST_IOSTREAMS_WINDOWS !::CloseHandle(handle_) #else ::close(handle_) != 0 #endif || error; clear(error); if (error) throw_system_failure("failed closing mapped file"); } void mapped_file_impl::resize(stream_offset new_size) { if (!is_open()) boost::throw_exception(BOOST_IOSTREAMS_FAILURE("file is closed")); if (flags() & mapped_file::priv) boost::throw_exception( BOOST_IOSTREAMS_FAILURE("can't resize private mapped file") ); if (!(flags() & mapped_file::readwrite)) boost::throw_exception( BOOST_IOSTREAMS_FAILURE("can't resize readonly mapped file") ); if (params_.offset >= new_size) boost::throw_exception( BOOST_IOSTREAMS_FAILURE("can't resize below mapped offset") ); if (!unmap_file()) cleanup_and_throw("failed unmapping file"); #ifdef BOOST_IOSTREAMS_WINDOWS stream_offset offset = ::SetFilePointer(handle_, 0, NULL, FILE_CURRENT); if (offset == INVALID_SET_FILE_POINTER && ::GetLastError() != NO_ERROR) cleanup_and_throw("failed querying file pointer"); LONG sizehigh = (new_size >> (sizeof(LONG) * 8)); LONG sizelow = (new_size & 0xffffffff); DWORD result = ::SetFilePointer(handle_, sizelow, &sizehigh, FILE_BEGIN); if ((result == INVALID_SET_FILE_POINTER && ::GetLastError() != NO_ERROR) || !::SetEndOfFile(handle_)) cleanup_and_throw("failed resizing mapped file"); sizehigh = (offset >> (sizeof(LONG) * 8)); sizelow = (offset & 0xffffffff); ::SetFilePointer(handle_, sizelow, &sizehigh, FILE_BEGIN); #else if (BOOST_IOSTREAMS_FD_TRUNCATE(handle_, new_size) == -1) cleanup_and_throw("failed resizing mapped file"); #endif size_ = new_size; param_type p(params_); map_file(p); // May modify p.hint params_ = p; } int mapped_file_impl::alignment() { #ifdef BOOST_IOSTREAMS_WINDOWS SYSTEM_INFO info; ::GetSystemInfo(&info); return static_cast(info.dwAllocationGranularity); #else return static_cast(sysconf(_SC_PAGESIZE)); #endif } void mapped_file_impl::open_file(param_type p) { bool readonly = p.flags != mapped_file::readwrite; #ifdef BOOST_IOSTREAMS_WINDOWS // Open file DWORD dwDesiredAccess = readonly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE); DWORD dwCreationDisposition = (p.new_file_size != 0 && !readonly) ? CREATE_ALWAYS : OPEN_EXISTING; DWORD dwFlagsandAttributes = readonly ? FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_TEMPORARY; handle_ = p.path.is_wide() ? ::CreateFileW( p.path.c_wstr(), dwDesiredAccess, FILE_SHARE_READ, NULL, dwCreationDisposition, dwFlagsandAttributes, NULL ) : ::CreateFileA( p.path.c_str(), dwDesiredAccess, FILE_SHARE_READ, NULL, dwCreationDisposition, dwFlagsandAttributes, NULL ); if (handle_ == INVALID_HANDLE_VALUE) cleanup_and_throw("failed opening file"); // Set file size if (p.new_file_size != 0 && !readonly) { LONG sizehigh = (p.new_file_size >> (sizeof(LONG) * 8)); LONG sizelow = (p.new_file_size & 0xffffffff); DWORD result = ::SetFilePointer(handle_, sizelow, &sizehigh, FILE_BEGIN); if ((result == INVALID_SET_FILE_POINTER && ::GetLastError() != NO_ERROR) || !::SetEndOfFile(handle_)) cleanup_and_throw("failed setting file size"); } // Determine file size. Dynamically locate GetFileSizeEx for compatibility // with old Platform SDK (thanks to Pavel Vozenilik). typedef BOOL (WINAPI *func)(HANDLE, PLARGE_INTEGER); HMODULE hmod = ::GetModuleHandleA("kernel32.dll"); func get_size = reinterpret_cast(::GetProcAddress(hmod, "GetFileSizeEx")); if (get_size) { LARGE_INTEGER info; if (get_size(handle_, &info)) { boost::intmax_t size = ( (static_cast(info.HighPart) << 32) | info.LowPart ); size_ = static_cast( p.length != max_length ? std::min(p.length, size) : size ); } else { cleanup_and_throw("failed querying file size"); return; } } else { DWORD hi; DWORD low; if ( (low = ::GetFileSize(handle_, &hi)) != INVALID_FILE_SIZE ) { boost::intmax_t size = (static_cast(hi) << 32) | low; size_ = static_cast( p.length != max_length ? std::min(p.length, size) : size ); } else { cleanup_and_throw("failed querying file size"); return; } } #else // #ifdef BOOST_IOSTREAMS_WINDOWS // Open file int flags = (readonly ? O_RDONLY : O_RDWR); if (p.new_file_size != 0 && !readonly) flags |= (O_CREAT | O_TRUNC); #ifdef _LARGEFILE64_SOURCE flags |= O_LARGEFILE; #endif errno = 0; handle_ = ::open(p.path.c_str(), flags, S_IRWXU); if (errno != 0) cleanup_and_throw("failed opening file"); //--------------Set file size---------------------------------------------// if (p.new_file_size != 0 && !readonly) if (BOOST_IOSTREAMS_FD_TRUNCATE(handle_, p.new_file_size) == -1) cleanup_and_throw("failed setting file size"); //--------------Determine file size---------------------------------------// bool success = true; if (p.length != max_length) { size_ = p.length; } else { struct BOOST_IOSTREAMS_FD_STAT info; success = ::BOOST_IOSTREAMS_FD_FSTAT(handle_, &info) != -1; size_ = info.st_size; } if (!success) cleanup_and_throw("failed querying file size"); #endif // #ifdef BOOST_IOSTREAMS_WINDOWS } void mapped_file_impl::try_map_file(param_type p) { bool priv = p.flags == mapped_file::priv; bool readonly = p.flags == mapped_file::readonly; #ifdef BOOST_IOSTREAMS_WINDOWS // Create mapping DWORD protect = priv ? PAGE_WRITECOPY : readonly ? PAGE_READONLY : PAGE_READWRITE; mapped_handle_ = ::CreateFileMappingA( handle_, NULL, protect, 0, 0, NULL ); if (mapped_handle_ == NULL) cleanup_and_throw("failed create mapping"); // Access data DWORD access = priv ? FILE_MAP_COPY : readonly ? FILE_MAP_READ : FILE_MAP_WRITE; void* data = ::MapViewOfFileEx( mapped_handle_, access, (DWORD) (p.offset >> 32), (DWORD) (p.offset & 0xffffffff), size_ != max_length ? size_ : 0, (LPVOID) p.hint ); if (!data) cleanup_and_throw("failed mapping view"); #else void* data = ::BOOST_IOSTREAMS_FD_MMAP( const_cast(p.hint), size_, readonly ? PROT_READ : (PROT_READ | PROT_WRITE), priv ? MAP_PRIVATE : MAP_SHARED, handle_, p.offset ); if (data == MAP_FAILED) cleanup_and_throw("failed mapping file"); #endif data_ = static_cast(data); } void mapped_file_impl::map_file(param_type& p) { try { try_map_file(p); } catch (const std::exception& e) { if (p.hint) { p.hint = 0; try_map_file(p); } else { boost::throw_exception(e); } } } bool mapped_file_impl::unmap_file() { #ifdef BOOST_IOSTREAMS_WINDOWS bool error = false; error = !::UnmapViewOfFile(data_) || error; error = !::CloseHandle(mapped_handle_) || error; mapped_handle_ = NULL; return !error; #else return ::munmap(data_, size_) == 0; #endif } void mapped_file_impl::clear(bool error) { params_ = param_type(); data_ = 0; size_ = 0; #ifdef BOOST_IOSTREAMS_WINDOWS handle_ = INVALID_HANDLE_VALUE; mapped_handle_ = NULL; #else handle_ = 0; #endif error_ = error; } // Called when an error is encountered during the execution of open_file or // map_file void mapped_file_impl::cleanup_and_throw(const char* msg) { #ifdef BOOST_IOSTREAMS_WINDOWS DWORD error = GetLastError(); if (mapped_handle_ != NULL) ::CloseHandle(mapped_handle_); if (handle_ != INVALID_HANDLE_VALUE) ::CloseHandle(handle_); SetLastError(error); #else int error = errno; if (handle_ != 0) ::close(handle_); errno = error; #endif clear(true); boost::iostreams::detail::throw_system_failure(msg); } //------------------Implementation of mapped_file_params_base-----------------// void mapped_file_params_base::normalize() { if (mode && flags) boost::throw_exception(BOOST_IOSTREAMS_FAILURE( "at most one of 'mode' and 'flags' may be specified" )); if (flags) { switch (flags) { case mapped_file::readonly: case mapped_file::readwrite: case mapped_file::priv: break; default: boost::throw_exception(BOOST_IOSTREAMS_FAILURE("invalid flags")); } } else { flags = (mode & BOOST_IOS::out) ? mapped_file::readwrite : mapped_file::readonly; mode = BOOST_IOS::openmode(); } if (offset < 0) boost::throw_exception(BOOST_IOSTREAMS_FAILURE("invalid offset")); if (new_file_size < 0) boost::throw_exception( BOOST_IOSTREAMS_FAILURE("invalid new file size") ); } } // End namespace detail. //------------------Implementation of mapped_file_source----------------------// mapped_file_source::mapped_file_source() : pimpl_(new impl_type) { } mapped_file_source::mapped_file_source(const mapped_file_source& other) : pimpl_(other.pimpl_) { } bool mapped_file_source::is_open() const { return pimpl_->is_open(); } void mapped_file_source::close() { pimpl_->close(); } // safe_bool is explicitly qualified below to please msvc 7.1 mapped_file_source::operator mapped_file_source::safe_bool() const { return pimpl_->error() ? &safe_bool_helper::x : 0; } bool mapped_file_source::operator!() const { return pimpl_->error(); } mapped_file_source::mapmode mapped_file_source::flags() const { return pimpl_->flags(); } mapped_file_source::size_type mapped_file_source::size() const { return pimpl_->size(); } const char* mapped_file_source::data() const { return pimpl_->data(); } const char* mapped_file_source::begin() const { return data(); } const char* mapped_file_source::end() const { return data() + size(); } int mapped_file_source::alignment() { return detail::mapped_file_impl::alignment(); } void mapped_file_source::init() { pimpl_.reset(new impl_type); } void mapped_file_source::open_impl(const param_type& p) { pimpl_->open(p); } //------------------Implementation of mapped_file-----------------------------// mapped_file::mapped_file(const mapped_file& other) : delegate_(other.delegate_) { } void mapped_file::resize(stream_offset new_size) { delegate_.pimpl_->resize(new_size); } //------------------Implementation of mapped_file_sink------------------------// mapped_file_sink::mapped_file_sink(const mapped_file_sink& other) : mapped_file(static_cast(other)) { } //----------------------------------------------------------------------------// } } // End namespaces iostreams, boost.