1// (C) Copyright Craig Henderson 2002 'boost/memmap.hpp' from sandbox
2// (C) Copyright Jonathan Turkanis 2004.
3// (C) Copyright Jonathan Graehl 2004.
4// (C) Copyright Jorge Lodos 2008.
5// Distributed under the Boost Software License, Version 1.0. (See accompanying
6// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
7
8// Define BOOST_IOSTREAMS_SOURCE so that <boost/iostreams/detail/config.hpp>
9// knows that we are building the library (possibly exporting code), rather
10// than using it (possibly importing code).
11#define BOOST_IOSTREAMS_SOURCE
12
13#include <cassert>
14#include <stdexcept>
15#include <boost/iostreams/detail/config/rtl.hpp>
16#include <boost/iostreams/detail/config/windows_posix.hpp>
17#include <boost/iostreams/detail/file_handle.hpp>
18#include <boost/iostreams/detail/system_failure.hpp>
19#include <boost/iostreams/device/mapped_file.hpp>
20#include <boost/throw_exception.hpp>
21#include <boost/numeric/conversion/cast.hpp>
22
23#ifdef BOOST_IOSTREAMS_WINDOWS
24# define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
25# include <windows.h>
26#else
27# include <errno.h>
28# include <fcntl.h>
29# include <sys/mman.h> // mmap, munmap.
30# include <sys/stat.h>
31# include <sys/types.h> // struct stat.
32# include <unistd.h> // sysconf.
33#endif
34
35namespace boost { namespace iostreams {
36
37namespace detail {
38
39// Class containing the platform-sepecific implementation
40// Invariant: The members params_, data_, size_, handle_ (and mapped_handle_
41// on Windows) either
42// - all have default values (or INVALID_HANDLE_VALUE for
43// Windows handles), or
44// - all have values reflecting a successful mapping.
45// In the first case, error_ may be true, reflecting a recent unsuccessful
46// open or close attempt; in the second case, error_ is always false.
47class mapped_file_impl {
48public:
49 typedef mapped_file_source::size_type size_type;
50 typedef mapped_file_source::param_type param_type;
51 typedef mapped_file_source::mapmode mapmode;
52 BOOST_STATIC_CONSTANT(
53 size_type, max_length = mapped_file_source::max_length);
54 mapped_file_impl();
55 ~mapped_file_impl();
56 void open(param_type p);
57 bool is_open() const { return data_ != 0; }
58 void close();
59 bool error() const { return error_; }
60 mapmode flags() const { return params_.flags; }
61 std::size_t size() const { return static_cast<std::size_t>(size_); }
62 char* data() const { return data_; }
63 void resize(stream_offset new_size);
64 static int alignment();
65private:
66 void open_file(param_type p);
67 void try_map_file(param_type p);
68 void map_file(param_type& p);
69 bool unmap_file();
70 void clear(bool error);
71 void cleanup_and_throw(const char* msg);
72 param_type params_;
73 char* data_;
74 stream_offset size_;
75 file_handle handle_;
76#ifdef BOOST_IOSTREAMS_WINDOWS
77 file_handle mapped_handle_;
78#endif
79 bool error_;
80};
81
82mapped_file_impl::mapped_file_impl() { clear(error: false); }
83
84mapped_file_impl::~mapped_file_impl()
85{ try { close(); } catch (...) { } }
86
87void mapped_file_impl::open(param_type p)
88{
89 if (is_open())
90 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("file already open"));
91 p.normalize();
92 open_file(p);
93 map_file(p); // May modify p.hint
94 params_ = p;
95}
96
97void mapped_file_impl::close()
98{
99 if (data_ == 0)
100 return;
101 bool error = false;
102 error = !unmap_file() || error;
103 error =
104 #ifdef BOOST_IOSTREAMS_WINDOWS
105 !::CloseHandle(handle_)
106 #else
107 ::close(fd: handle_) != 0
108 #endif
109 || error;
110 clear(error);
111 if (error)
112 throw_system_failure(msg: "failed closing mapped file");
113}
114
115void mapped_file_impl::resize(stream_offset new_size)
116{
117 if (!is_open())
118 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("file is closed"));
119 if (flags() & mapped_file::priv)
120 boost::throw_exception(
121 BOOST_IOSTREAMS_FAILURE("can't resize private mapped file")
122 );
123 if (!(flags() & mapped_file::readwrite))
124 boost::throw_exception(
125 BOOST_IOSTREAMS_FAILURE("can't resize readonly mapped file")
126 );
127 if (params_.offset >= new_size)
128 boost::throw_exception(
129 BOOST_IOSTREAMS_FAILURE("can't resize below mapped offset")
130 );
131 if (!unmap_file())
132 cleanup_and_throw(msg: "failed unmapping file");
133#ifdef BOOST_IOSTREAMS_WINDOWS
134 stream_offset offset = ::SetFilePointer(handle_, 0, NULL, FILE_CURRENT);
135 if (offset == INVALID_SET_FILE_POINTER && ::GetLastError() != NO_ERROR)
136 cleanup_and_throw("failed querying file pointer");
137 LONG sizehigh = (new_size >> (sizeof(LONG) * 8));
138 LONG sizelow = (new_size & 0xffffffff);
139 DWORD result = ::SetFilePointer(handle_, sizelow, &sizehigh, FILE_BEGIN);
140 if ((result == INVALID_SET_FILE_POINTER && ::GetLastError() != NO_ERROR)
141 || !::SetEndOfFile(handle_))
142 cleanup_and_throw("failed resizing mapped file");
143 sizehigh = (offset >> (sizeof(LONG) * 8));
144 sizelow = (offset & 0xffffffff);
145 ::SetFilePointer(handle_, sizelow, &sizehigh, FILE_BEGIN);
146#else
147 if (BOOST_IOSTREAMS_FD_TRUNCATE(fd: handle_, length: new_size) == -1)
148 cleanup_and_throw(msg: "failed resizing mapped file");
149#endif
150 size_ = new_size;
151 param_type p(params_);
152 map_file(p); // May modify p.hint
153 params_ = p;
154}
155
156int mapped_file_impl::alignment()
157{
158#ifdef BOOST_IOSTREAMS_WINDOWS
159 SYSTEM_INFO info;
160 ::GetSystemInfo(&info);
161 return static_cast<int>(info.dwAllocationGranularity);
162#else
163 return static_cast<int>(sysconf(_SC_PAGESIZE));
164#endif
165}
166
167void mapped_file_impl::open_file(param_type p)
168{
169 bool readonly = p.flags != mapped_file::readwrite;
170#ifdef BOOST_IOSTREAMS_WINDOWS
171
172 // Open file
173 DWORD dwDesiredAccess =
174 readonly ?
175 GENERIC_READ :
176 (GENERIC_READ | GENERIC_WRITE);
177 DWORD dwCreationDisposition = (p.new_file_size != 0 && !readonly) ?
178 CREATE_ALWAYS :
179 OPEN_EXISTING;
180 DWORD dwFlagsandAttributes =
181 readonly ?
182 FILE_ATTRIBUTE_READONLY :
183 FILE_ATTRIBUTE_TEMPORARY;
184 handle_ = p.path.is_wide() ?
185 ::CreateFileW(
186 p.path.c_wstr(),
187 dwDesiredAccess,
188 FILE_SHARE_READ,
189 NULL,
190 dwCreationDisposition,
191 dwFlagsandAttributes,
192 NULL ) :
193 ::CreateFileA(
194 p.path.c_str(),
195 dwDesiredAccess,
196 FILE_SHARE_READ,
197 NULL,
198 dwCreationDisposition,
199 dwFlagsandAttributes,
200 NULL );
201 if (handle_ == INVALID_HANDLE_VALUE)
202 cleanup_and_throw("failed opening file");
203
204 // Set file size
205 if (p.new_file_size != 0 && !readonly) {
206 LONG sizehigh = (p.new_file_size >> (sizeof(LONG) * 8));
207 LONG sizelow = (p.new_file_size & 0xffffffff);
208 DWORD result = ::SetFilePointer(handle_, sizelow, &sizehigh, FILE_BEGIN);
209 if ((result == INVALID_SET_FILE_POINTER && ::GetLastError() != NO_ERROR)
210 || !::SetEndOfFile(handle_))
211 cleanup_and_throw("failed setting file size");
212 }
213
214 // Determine file size. Dynamically locate GetFileSizeEx for compatibility
215 // with old Platform SDK (thanks to Pavel Vozenilik).
216 typedef BOOL (WINAPI *func)(HANDLE, PLARGE_INTEGER);
217 HMODULE hmod = ::GetModuleHandleA("kernel32.dll");
218 func get_size =
219 reinterpret_cast<func>(::GetProcAddress(hmod, "GetFileSizeEx"));
220 if (get_size) {
221 LARGE_INTEGER info;
222 if (get_size(handle_, &info)) {
223 boost::intmax_t size =
224 ( (static_cast<boost::intmax_t>(info.HighPart) << 32) |
225 info.LowPart );
226 size_ =
227 static_cast<std::size_t>(
228 p.length != max_length ?
229 std::min<boost::intmax_t>(p.length, size) :
230 size
231 );
232 } else {
233 cleanup_and_throw("failed querying file size");
234 return;
235 }
236 } else {
237 DWORD hi;
238 DWORD low;
239 if ( (low = ::GetFileSize(handle_, &hi))
240 !=
241 INVALID_FILE_SIZE )
242 {
243 boost::intmax_t size =
244 (static_cast<boost::intmax_t>(hi) << 32) | low;
245 size_ =
246 static_cast<std::size_t>(
247 p.length != max_length ?
248 std::min<boost::intmax_t>(p.length, size) :
249 size
250 );
251 } else {
252 cleanup_and_throw("failed querying file size");
253 return;
254 }
255 }
256#else // #ifdef BOOST_IOSTREAMS_WINDOWS
257
258 // Open file
259 int flags = (readonly ? O_RDONLY : O_RDWR);
260 if (p.new_file_size != 0 && !readonly)
261 flags |= (O_CREAT | O_TRUNC);
262 #ifdef _LARGEFILE64_SOURCE
263 flags |= O_LARGEFILE;
264 #endif
265 errno = 0;
266 if (p.path.is_wide()) { errno = EINVAL; cleanup_and_throw(msg: "wide path not supported here"); } // happens on CYGWIN
267 handle_ = ::open(file: p.path.c_str(), oflag: flags, S_IRWXU);
268 if (errno != 0)
269 cleanup_and_throw(msg: "failed opening file");
270
271 //--------------Set file size---------------------------------------------//
272
273 if (p.new_file_size != 0 && !readonly)
274 if (BOOST_IOSTREAMS_FD_TRUNCATE(fd: handle_, length: p.new_file_size) == -1)
275 cleanup_and_throw(msg: "failed setting file size");
276
277 //--------------Determine file size---------------------------------------//
278
279 bool success = true;
280 if (p.length != max_length) {
281 size_ = p.length;
282 } else {
283 struct BOOST_IOSTREAMS_FD_STAT info;
284 success = ::BOOST_IOSTREAMS_FD_FSTAT(fd: handle_, buf: &info) != -1;
285 size_ = info.st_size;
286 }
287 if (!success)
288 cleanup_and_throw(msg: "failed querying file size");
289#endif // #ifdef BOOST_IOSTREAMS_WINDOWS
290}
291
292void mapped_file_impl::try_map_file(param_type p)
293{
294 bool priv = p.flags == mapped_file::priv;
295 bool readonly = p.flags == mapped_file::readonly;
296#ifdef BOOST_IOSTREAMS_WINDOWS
297
298 // Create mapping
299 DWORD protect = priv ?
300 PAGE_WRITECOPY :
301 readonly ?
302 PAGE_READONLY :
303 PAGE_READWRITE;
304 mapped_handle_ =
305 ::CreateFileMappingA(
306 handle_,
307 NULL,
308 protect,
309 0,
310 0,
311 NULL );
312 if (mapped_handle_ == NULL)
313 cleanup_and_throw("failed create mapping");
314
315 // Access data
316 DWORD access = priv ?
317 FILE_MAP_COPY :
318 readonly ?
319 FILE_MAP_READ :
320 FILE_MAP_WRITE;
321 void* data =
322 ::MapViewOfFileEx(
323 mapped_handle_,
324 access,
325 (DWORD) (p.offset >> 32),
326 (DWORD) (p.offset & 0xffffffff),
327 (SIZE_T) (numeric_cast<size_type>(size_) != max_length ? size_ : 0),
328 (LPVOID) p.hint );
329 if (!data)
330 cleanup_and_throw("failed mapping view");
331#else
332 void* data =
333 ::BOOST_IOSTREAMS_FD_MMAP(
334 addr: const_cast<char*>(p.hint),
335 len: size_,
336 prot: readonly ? PROT_READ : (PROT_READ | PROT_WRITE),
337 flags: priv ? MAP_PRIVATE : MAP_SHARED,
338 fd: handle_,
339 offset: p.offset );
340 if (data == MAP_FAILED)
341 cleanup_and_throw(msg: "failed mapping file");
342#endif
343 data_ = static_cast<char*>(data);
344}
345
346void mapped_file_impl::map_file(param_type& p)
347{
348 try {
349 try_map_file(p);
350 } catch (const std::exception&) {
351 if (p.hint) {
352 p.hint = 0;
353 try_map_file(p);
354 } else {
355 throw;
356 }
357 }
358}
359
360bool mapped_file_impl::unmap_file()
361{
362#ifdef BOOST_IOSTREAMS_WINDOWS
363 bool error = false;
364 error = !::UnmapViewOfFile(data_) || error;
365 error = !::CloseHandle(mapped_handle_) || error;
366 mapped_handle_ = NULL;
367 return !error;
368#else
369 return ::munmap(addr: data_, len: size_) == 0;
370#endif
371}
372
373void mapped_file_impl::clear(bool error)
374{
375 params_ = param_type();
376 data_ = 0;
377 size_ = 0;
378#ifdef BOOST_IOSTREAMS_WINDOWS
379 handle_ = INVALID_HANDLE_VALUE;
380 mapped_handle_ = NULL;
381#else
382 handle_ = 0;
383#endif
384 error_ = error;
385}
386
387// Called when an error is encountered during the execution of open_file or
388// map_file
389void mapped_file_impl::cleanup_and_throw(const char* msg)
390{
391#ifdef BOOST_IOSTREAMS_WINDOWS
392 DWORD error = GetLastError();
393 if (mapped_handle_ != NULL)
394 ::CloseHandle(mapped_handle_);
395 if (handle_ != INVALID_HANDLE_VALUE)
396 ::CloseHandle(handle_);
397 SetLastError(error);
398#else
399 int error = errno;
400 if (handle_ != 0)
401 ::close(fd: handle_);
402 errno = error;
403#endif
404 clear(error: true);
405 boost::iostreams::detail::throw_system_failure(msg);
406}
407
408//------------------Implementation of mapped_file_params_base-----------------//
409
410void mapped_file_params_base::normalize()
411{
412 if (mode && flags)
413 boost::throw_exception(BOOST_IOSTREAMS_FAILURE(
414 "at most one of 'mode' and 'flags' may be specified"
415 ));
416 if (flags) {
417 switch (flags) {
418 case mapped_file::readonly:
419 case mapped_file::readwrite:
420 case mapped_file::priv:
421 break;
422 default:
423 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("invalid flags"));
424 }
425 } else {
426 flags = (mode & BOOST_IOS::out) ?
427 mapped_file::readwrite :
428 mapped_file::readonly;
429 mode = BOOST_IOS::openmode();
430 }
431 if (offset < 0)
432 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("invalid offset"));
433 if (new_file_size < 0)
434 boost::throw_exception(
435 BOOST_IOSTREAMS_FAILURE("invalid new file size")
436 );
437}
438
439} // End namespace detail.
440
441//------------------Implementation of mapped_file_source----------------------//
442
443mapped_file_source::mapped_file_source()
444 : pimpl_(new impl_type)
445 { }
446
447mapped_file_source::mapped_file_source(const mapped_file_source& other)
448 : pimpl_(other.pimpl_)
449 { }
450
451bool mapped_file_source::is_open() const
452{ return pimpl_->is_open(); }
453
454void mapped_file_source::close() { pimpl_->close(); }
455
456// safe_bool is explicitly qualified below to please msvc 7.1
457mapped_file_source::operator mapped_file_source::safe_bool() const
458{ return pimpl_->error() ? &safe_bool_helper::x : 0; }
459
460bool mapped_file_source::operator!() const
461{ return pimpl_->error(); }
462
463mapped_file_source::mapmode mapped_file_source::flags() const
464{ return pimpl_->flags(); }
465
466mapped_file_source::size_type mapped_file_source::size() const
467{ return pimpl_->size(); }
468
469const char* mapped_file_source::data() const { return pimpl_->data(); }
470
471const char* mapped_file_source::begin() const { return data(); }
472
473const char* mapped_file_source::end() const { return data() + size(); }
474int mapped_file_source::alignment()
475{ return detail::mapped_file_impl::alignment(); }
476
477void mapped_file_source::init() { pimpl_.reset(p: new impl_type); }
478
479void mapped_file_source::open_impl(const param_type& p)
480{ pimpl_->open(p); }
481
482//------------------Implementation of mapped_file-----------------------------//
483
484mapped_file::mapped_file(const mapped_file& other)
485 : delegate_(other.delegate_)
486 { }
487
488void mapped_file::resize(stream_offset new_size)
489{ delegate_.pimpl_->resize(new_size); }
490
491//------------------Implementation of mapped_file_sink------------------------//
492
493mapped_file_sink::mapped_file_sink(const mapped_file_sink& other)
494 : mapped_file(static_cast<const mapped_file&>(other))
495 { }
496
497//----------------------------------------------------------------------------//
498
499} } // End namespaces iostreams, boost.
500

source code of boost/libs/iostreams/src/mapped_file.cpp