From 57058ab20f6a20bb7497089bcc3067d241c7ee84 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Wed, 27 Apr 2016 17:04:56 +1000 Subject: [PATCH] io_win32: make mapping checks more robust fixes zero length mapping errors --- io_win32.cpp | 92 ++++++++++++++++++++++++++++++++++++++++------------ io_win32.hpp | 4 +-- 2 files changed, 74 insertions(+), 22 deletions(-) diff --git a/io_win32.cpp b/io_win32.cpp index db2baf01..fdd86f2d 100644 --- a/io_win32.cpp +++ b/io_win32.cpp @@ -25,8 +25,9 @@ using util::detail::win32::mapped_file; //----------------------------------------------------------------------------- -static DWORD -fflags_to_native (int flags) { +static constexpr +DWORD +fflags_to_generic (int flags) { switch (flags) { case O_RDONLY: return GENERIC_READ; case O_WRONLY: return GENERIC_WRITE; @@ -36,48 +37,99 @@ fflags_to_native (int flags) { unreachable (); } + +static constexpr +DWORD +fflags_to_access (int fflags) +{ + switch (fflags) { + case O_RDONLY: return FILE_MAP_READ; + case O_WRONLY: return FILE_MAP_WRITE; + case O_RDWR: return FILE_MAP_WRITE; + } + + unreachable (); +} + + +static constexpr +DWORD +mflags_to_protect (int mflags) { + DWORD res = 0; + + if (mflags & util::detail::win32::PROT_READ) res |= PAGE_READONLY; + if (mflags & util::detail::win32::PROT_WRITE) res |= PAGE_READWRITE; + if (mflags & util::detail::win32::PROT_EXEC) res |= PAGE_EXECUTE; + + return res; +} + + //----------------------------------------------------------------------------- mapped_file::mapped_file (const boost::filesystem::path &path, int fflags, int mflags): m_data (nullptr, UnmapViewOfFile) { + // Cache the ASCII path to avoid memory scoping issues. + std::string path_str = path.string (); + + // Get hold of the file we're attempting to map. Emulate some level of POXIS mmap. m_file.reset ( CreateFile ( - path.string ().c_str (), - fflags_to_native (fflags), - fflags & O_RDONLY ? FILE_SHARE_READ : 0, + path_str.c_str (), + fflags_to_generic (fflags), + fflags == O_RDONLY ? FILE_SHARE_READ : 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, - 0 + nullptr ) ); if (m_file == INVALID_HANDLE_VALUE) win32_error::throw_code (); - m_mapping.reset (CreateFileMapping (m_file, - nullptr, - access == O_RDONLY ? PAGE_READONLY : PAGE_READWRITE, - 0, 0, - nullptr)); + // I would rather perform checks on filesize after mapping, but mapping + // requires a check for empty files before we perform the mapping to + // detect errors it throws in that specific situation. + DWORD hi_size, lo_size = GetFileSize (m_file, &hi_size); + m_size = (uint64_t)hi_size << 32 | lo_size; - if (m_mapping == INVALID_HANDLE_VALUE) - win32_error::throw_code (); + m_mapping.reset ( + CreateFileMapping ( + m_file, + nullptr, + mflags_to_protect (mflags), + 0, 0, + nullptr + ) + ); - auto view = MapViewOfFile (m_mapping, - access == O_RDONLY ? FILE_MAP_READ : FILE_MAP_WRITE, - 0, 0, - 0); - if (view == nullptr) + // Apparently Windows lacks the ability to map zero length files; fucking + // hell. Try not to collapse, but instead bail with a null mapping and + // pray the user doesn't do something stupid with the result. + if (!m_mapping) { + auto err = win32_error::last_code (); + if (err == ERROR_FILE_INVALID && m_size == 0) + return; + + win32_error::throw_code (err); + } + + auto view = MapViewOfFile ( + m_mapping, + fflags_to_access (fflags), + 0, 0, + 0 + ); + + if (!view) win32_error::throw_code (); m_data.reset ( static_cast (view) ); - - m_size = GetFileSize (m_file, nullptr); } diff --git a/io_win32.hpp b/io_win32.hpp index 8a1af4d6..8435cfca 100644 --- a/io_win32.hpp +++ b/io_win32.hpp @@ -38,7 +38,7 @@ namespace util { class mapped_file { public: mapped_file (const boost::filesystem::path &path, - int fflags = O_RDONLY | O_BINARY, + int fflags = O_RDONLY, int mflags = PROT_READ); mapped_file (const mapped_file&) = delete; @@ -59,7 +59,7 @@ namespace util { ::util::win32::handle m_mapping; std::unique_ptr m_data; - size_t m_size; + uint64_t m_size; }; } }