// Copyright (c) 2014, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ #include "../config.h" #include "libaegisub/file_mapping.h" #include "libaegisub/fs.h" #include "libaegisub/util.h" #include #include #include #ifdef _WIN32 #include #endif using namespace boost::interprocess; namespace { char *map(int64_t s_offset, uint64_t length, boost::interprocess::mode_t mode, uint64_t file_size, agi::file_mapping const& file, std::unique_ptr& region, uint64_t& mapping_start) { auto offset = static_cast(s_offset); if (offset + length > file_size) throw agi::InternalError("Attempted to map beyond end of file", nullptr); // Check if we can just use the current mapping if (region && offset >= mapping_start && offset + length <= mapping_start + region->get_size()) return static_cast(region->get_address()) + offset - mapping_start; if (sizeof(size_t) == 4) { mapping_start = offset & ~0xFFFFFULL; // Align to 1 MB bondary length += static_cast(offset - mapping_start); // Map 16 MB or length rounded up to the next MB length = std::min(std::max(0x1000000U, (length + 0xFFFFF) & ~0xFFFFF), file_size - mapping_start); } else { // Just map the whole file mapping_start = 0; length = file_size; } if (length > std::numeric_limits::max()) throw std::bad_alloc(); try { region = agi::util::make_unique(file, mode, mapping_start, static_cast(length)); } catch (interprocess_exception const&) { throw agi::fs::FileSystemUnknownError("Failed mapping a view of the file"); } return static_cast(region->get_address()) + offset - mapping_start; } } namespace agi { file_mapping::file_mapping(fs::path const& filename, bool temporary) #ifdef _WIN32 : handle(CreateFileW(filename.wstring().c_str(), temporary ? read_write : read_only, temporary ? FILE_SHARE_READ | FILE_SHARE_WRITE : FILE_SHARE_READ, nullptr, temporary ? OPEN_ALWAYS : OPEN_EXISTING, 0, 0)) { if (handle == ipcdetail::invalid_file()) { switch (GetLastError()) { case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: throw fs::FileNotFound(filename); case ERROR_ACCESS_DENIED: throw fs::ReadDenied(filename); default: throw fs::FileSystemUnknownError(util::ErrorString(GetLastError())); } } #else : handle(temporary ? ipcdetail::create_or_open_file(filename.string().c_str(), read_write) : ipcdetail::open_existing_file(filename.string().c_str(), read_only)) { if (handle == ipcdetail::invalid_file()) { switch (errno) { case ENOENT: throw fs::FileNotFound(filename); case EACCES: throw fs::ReadDenied(filename); case EIO: throw fs::FileSystemUnknownError("Fatal I/O opening path: " + filename.string()); } } #endif } file_mapping::~file_mapping() { if (handle != ipcdetail::invalid_file()) { ipcdetail::close_file(handle); } } read_file_mapping::read_file_mapping(fs::path const& filename) : file(filename, false) { offset_t size; ipcdetail::get_file_size(file.get_mapping_handle().handle, size); file_size = static_cast(size); } read_file_mapping::~read_file_mapping() { } const char *read_file_mapping::read() { return read(0, size()); } const char *read_file_mapping::read(int64_t offset, uint64_t length) { return map(offset, length, read_only, file_size, file, region, mapping_start); } temp_file_mapping::temp_file_mapping(fs::path const& filename, uint64_t size) : file(filename, true) , file_size(size) { auto handle = file.get_mapping_handle().handle; #ifdef _WIN32 LARGE_INTEGER li; li.QuadPart = size; SetFilePointerEx(handle, li, nullptr, FILE_BEGIN); SetEndOfFile(handle); #else ftruncate(handle, size); unlink(filename.string().c_str()); #endif } temp_file_mapping::~temp_file_mapping() { } const char *temp_file_mapping::read(int64_t offset, uint64_t length) { return write(offset, length); } char *temp_file_mapping::write(int64_t offset, uint64_t length) { return map(offset, length, read_write, file_size, file, region, mapping_start); } }