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