Ticket #6576: file_descriptor.cpp

File file_descriptor.cpp, 20.3 KB (added by anonymous, 11 years ago)

fixed version of file_descriptor.cpp

Line 
1// (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com)
2// (C) Copyright 2003-2007 Jonathan Turkanis
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
5
6// See http://www.boost.org/libs/iostreams for documentation.
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 <cerrno>
15#include <cstdio> // SEEK_SET, etc.
16#include <boost/config.hpp> // BOOST_JOIN
17#include <boost/iostreams/detail/error.hpp>
18#include <boost/iostreams/detail/config/dyn_link.hpp>
19#include <boost/iostreams/detail/config/rtl.hpp> // BOOST_IOSTREAMS_FD_XXX
20#include <boost/iostreams/detail/config/windows_posix.hpp>
21#include <boost/iostreams/detail/system_failure.hpp>
22#include <boost/iostreams/detail/ios.hpp> // openmodes, failure.
23#include <boost/iostreams/device/file_descriptor.hpp>
24#include <boost/integer_traits.hpp>
25#include <boost/throw_exception.hpp>
26
27 // OS-specific headers for low-level i/o.
28
29#include <fcntl.h> // file opening flags.
30#include <sys/stat.h> // file access permissions.
31#ifdef BOOST_IOSTREAMS_WINDOWS
32# include <io.h> // low-level file i/o.
33# define WINDOWS_LEAN_AND_MEAN
34# include <windows.h>
35# ifndef INVALID_SET_FILE_POINTER
36# define INVALID_SET_FILE_POINTER ((DWORD)-1)
37# endif
38#else
39# include <sys/types.h> // mode_t.
40# include <unistd.h> // low-level file i/o.
41#endif
42
43namespace boost { namespace iostreams {
44
45//------------------Definition of file_descriptor_impl------------------------//
46
47namespace detail {
48
49// Contains the platform dependant implementation
50struct file_descriptor_impl {
51 // Note: These need to match file_desciptor_flags
52 enum flags {
53 never_close = 0,
54 close_on_exit = 1,
55 close_on_close = 2,
56 close_always = 3
57 };
58
59 file_descriptor_impl();
60 ~file_descriptor_impl();
61 void open(file_handle fd, flags);
62#ifdef BOOST_IOSTREAMS_WINDOWS
63 void open(int fd, flags);
64#endif
65 void open(const detail::path&, BOOST_IOS::openmode);
66 bool is_open() const;
67 void close();
68 void close_impl(bool close_flag, bool throw_);
69 std::streamsize read(char* s, std::streamsize n);
70 std::streamsize write(const char* s, std::streamsize n);
71 std::streampos seek(stream_offset off, BOOST_IOS::seekdir way);
72 static file_handle invalid_handle();
73 file_handle handle_;
74 int flags_;
75};
76
77//------------------Implementation of file_descriptor_impl--------------------//
78
79file_descriptor_impl::file_descriptor_impl()
80 : handle_(invalid_handle()), flags_(0)
81 { }
82
83file_descriptor_impl::~file_descriptor_impl()
84{
85 close_impl(flags_ & close_on_exit, false);
86}
87
88void file_descriptor_impl::open(file_handle fd, flags f)
89{
90 // Using 'close' to close the existing handle so that it will throw an
91 // exception if it fails.
92 //
93 // Only closing after assigning the new handle, so that the class will
94 // take ownership of the handle regardless of whether close throws.
95
96 file_descriptor_impl tmp;
97 tmp.handle_ = handle_;
98 tmp.flags_ = flags_ & close_on_exit ? close_on_close : never_close;
99
100 handle_ = fd;
101 flags_ = f;
102
103 tmp.close();
104}
105
106#ifdef BOOST_IOSTREAMS_WINDOWS //---------------------------------------------//
107
108void file_descriptor_impl::open(int fd, flags f)
109{ open(reinterpret_cast<file_handle>(_get_osfhandle(fd)), f); }
110
111#endif // #ifdef BOOST_IOSTREAMS_WINDOWS //-----------------------------------//
112
113void file_descriptor_impl::open(const detail::path& p, BOOST_IOS::openmode mode)
114{
115 close_impl(flags_ & close_on_exit, true);
116
117#ifdef BOOST_IOSTREAMS_WINDOWS //---------------------------------------------//
118 DWORD dwDesiredAccess;
119 DWORD dwCreationDisposition;
120 if ( (mode & (BOOST_IOS::in | BOOST_IOS::out))
121 ==
122 (BOOST_IOS::in | BOOST_IOS::out) )
123 {
124 if (mode & BOOST_IOS::app)
125 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("bad open mode"));
126 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
127 dwCreationDisposition =
128 (mode & BOOST_IOS::trunc) ?
129 CREATE_ALWAYS :
130 OPEN_EXISTING;
131 } else if (mode & BOOST_IOS::in) {
132 if (mode & (BOOST_IOS::app | BOOST_IOS::trunc))
133 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("bad open mode"));
134 dwDesiredAccess = GENERIC_READ;
135 dwCreationDisposition = OPEN_EXISTING;
136 } else if (mode & BOOST_IOS::out) {
137 if ( (mode & (BOOST_IOS::app | BOOST_IOS::trunc))
138 ==
139 (BOOST_IOS::app | BOOST_IOS::trunc) )
140 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("bad open mode"));
141 if (mode & BOOST_IOS::app) {
142 dwCreationDisposition = OPEN_ALWAYS;
143 dwDesiredAccess =
144 FILE_APPEND_DATA |
145 FILE_WRITE_ATTRIBUTES |
146 FILE_WRITE_EA |
147 STANDARD_RIGHTS_WRITE |
148 SYNCHRONIZE;
149 } else {
150 dwDesiredAccess = GENERIC_WRITE;
151 dwCreationDisposition = CREATE_ALWAYS;
152 }
153 } else {
154 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("bad open mode"));
155 }
156
157 HANDLE handle = p.is_wide() ?
158 ::CreateFileW( p.c_wstr(),
159 dwDesiredAccess,
160 FILE_SHARE_READ | FILE_SHARE_WRITE,
161 NULL, // lpSecurityAttributes
162 dwCreationDisposition,
163 FILE_ATTRIBUTE_NORMAL,
164 NULL ) : // hTemplateFile
165 ::CreateFileA( p.c_str(),
166 dwDesiredAccess,
167 FILE_SHARE_READ | FILE_SHARE_WRITE,
168 NULL, // lpSecurityAttributes
169 dwCreationDisposition,
170 FILE_ATTRIBUTE_NORMAL,
171 NULL ); // hTemplateFile
172 if (handle != INVALID_HANDLE_VALUE) {
173 handle_ = handle;
174 flags_ = close_always;
175 } else {
176 flags_ = 0;
177 throw_system_failure("failed opening file");
178 }
179#else // #ifdef BOOST_IOSTREAMS_WINDOWS //------------------------------------//
180
181 // Calculate oflag argument to open.
182
183 int oflag = 0;
184 if ( (mode & (BOOST_IOS::in | BOOST_IOS::out))
185 ==
186 (BOOST_IOS::in | BOOST_IOS::out) )
187 {
188 if( mode & BOOST_IOS::app )
189 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("bad open mode"));
190 oflag |= O_RDWR;
191 if( mode & BOOST_IOS::trunc ) {
192 oflag |= O_TRUNC;
193 oflag |= O_CREAT;
194 }
195 } else if (mode & BOOST_IOS::in) {
196 if( mode & (BOOST_IOS::app | BOOST_IOS::trunc) )
197 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("bad open mode"));
198 oflag |= O_RDONLY;
199 } else if (mode & BOOST_IOS::out) {
200 if( (mode & (BOOST_IOS::app | BOOST_IOS::trunc))
201 ==
202 (BOOST_IOS::app | BOOST_IOS::trunc) )
203 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("bad open mode"));
204 oflag |= O_WRONLY;
205 if (mode & BOOST_IOS::app)
206 oflag |= O_APPEND;
207 else {
208 oflag |= O_CREAT;
209 oflag |= O_TRUNC;
210 }
211 } else {
212 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("bad open mode"));
213 }
214 #ifdef _LARGEFILE64_SOURCE
215 oflag |= O_LARGEFILE;
216 #endif
217
218 // Calculate pmode argument to open.
219
220 mode_t pmode = S_IRUSR | S_IWUSR |
221 S_IRGRP | S_IWGRP |
222 S_IROTH | S_IWOTH;
223
224 // Open file.
225
226 int fd = BOOST_IOSTREAMS_FD_OPEN(p.c_str(), oflag, pmode);
227 if (fd == -1) {
228 boost::throw_exception(system_failure("failed opening file"));
229 } else {
230 handle_ = fd;
231 flags_ = close_always;
232 }
233#endif // #ifndef BOOST_IOSTREAMS_WINDOWS //----------------------------------//
234}
235
236bool file_descriptor_impl::is_open() const
237{ return handle_ != invalid_handle(); }
238
239void file_descriptor_impl::close()
240{
241 close_impl(flags_ & close_on_close, true);
242}
243
244void file_descriptor_impl::close_impl(bool close_flag, bool throw_) {
245 if (handle_ != invalid_handle()) {
246 if (close_flag) {
247 bool success =
248 #ifdef BOOST_IOSTREAMS_WINDOWS
249 ::CloseHandle(handle_) == 1;
250 #else
251 BOOST_IOSTREAMS_FD_CLOSE(handle_) != -1;
252 #endif
253 if (!success && throw_)
254 throw_system_failure("failed closing file");
255 }
256 handle_ = invalid_handle();
257 flags_ = 0;
258 }
259}
260
261std::streamsize file_descriptor_impl::read(char* s, std::streamsize n)
262{
263#ifdef BOOST_IOSTREAMS_WINDOWS
264 DWORD result;
265 if (!::ReadFile(handle_, s, n, &result, NULL))
266 {
267 // report EOF if the write-side of a pipe has been closed
268 if (GetLastError() == ERROR_BROKEN_PIPE)
269 {
270 result = 0;
271 }
272 else
273 throw_system_failure("failed reading");
274 }
275 return result == 0 ? -1 : static_cast<std::streamsize>(result);
276#else // #ifdef BOOST_IOSTREAMS_WINDOWS
277 errno = 0;
278 std::streamsize result = BOOST_IOSTREAMS_FD_READ(handle_, s, n);
279 if (errno != 0)
280 throw_system_failure("failed reading");
281 return result == 0 ? -1 : result;
282#endif // #ifdef BOOST_IOSTREAMS_WINDOWS
283}
284
285std::streamsize file_descriptor_impl::write(const char* s, std::streamsize n)
286{
287#ifdef BOOST_IOSTREAMS_WINDOWS
288 DWORD ignore;
289 if (!::WriteFile(handle_, s, n, &ignore, NULL))
290 throw_system_failure("failed writing");
291 return n;
292#else // #ifdef BOOST_IOSTREAMS_WINDOWS
293 int amt = BOOST_IOSTREAMS_FD_WRITE(handle_, s, n);
294 if (amt < n) // Handles blocking fd's only.
295 throw_system_failure("failed writing");
296 return n;
297#endif // #ifdef BOOST_IOSTREAMS_WINDOWS
298}
299
300std::streampos file_descriptor_impl::seek
301 (stream_offset off, BOOST_IOS::seekdir way)
302{
303#ifdef BOOST_IOSTREAMS_WINDOWS
304 LONG lDistanceToMove = static_cast<LONG>(off & 0xffffffff);
305 LONG lDistanceToMoveHigh = static_cast<LONG>(off >> 32);
306 DWORD dwResultLow =
307 ::SetFilePointer( handle_,
308 lDistanceToMove,
309 &lDistanceToMoveHigh,
310 way == BOOST_IOS::beg ?
311 FILE_BEGIN :
312 way == BOOST_IOS::cur ?
313 FILE_CURRENT :
314 FILE_END );
315 if ( dwResultLow == INVALID_SET_FILE_POINTER &&
316 ::GetLastError() != NO_ERROR )
317 {
318 boost::throw_exception(system_failure("failed seeking"));
319 } else {
320 return offset_to_position(
321 (stream_offset(lDistanceToMoveHigh) << 32) + dwResultLow
322 );
323 }
324#else // #ifdef BOOST_IOSTREAMS_WINDOWS
325 if ( off > integer_traits<BOOST_IOSTREAMS_FD_OFFSET>::const_max ||
326 off < integer_traits<BOOST_IOSTREAMS_FD_OFFSET>::const_min )
327 {
328 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("bad offset"));
329 }
330 stream_offset result =
331 BOOST_IOSTREAMS_FD_SEEK(
332 handle_,
333 static_cast<BOOST_IOSTREAMS_FD_OFFSET>(off),
334 ( way == BOOST_IOS::beg ?
335 SEEK_SET :
336 way == BOOST_IOS::cur ?
337 SEEK_CUR :
338 SEEK_END )
339 );
340 if (result == -1)
341 boost::throw_exception(system_failure("failed seeking"));
342 return offset_to_position(result);
343#endif // #ifdef BOOST_IOSTREAMS_WINDOWS
344}
345
346// Returns the value stored in a file_handle variable when no file is open
347file_handle file_descriptor_impl::invalid_handle()
348{
349#ifdef BOOST_IOSTREAMS_WINDOWS
350 return INVALID_HANDLE_VALUE;
351#else
352 return -1;
353#endif
354}
355
356} // End namespace detail.
357
358//------------------Implementation of file_descriptor-------------------------//
359
360file_descriptor::file_descriptor() : pimpl_(new impl_type) { }
361
362file_descriptor::file_descriptor(handle_type fd, file_descriptor_flags f)
363 : pimpl_(new impl_type)
364{ open(fd, f); }
365
366#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
367file_descriptor::file_descriptor(handle_type fd, bool close_on_exit)
368 : pimpl_(new impl_type)
369{ open(fd, close_on_exit); }
370#endif
371
372#ifdef BOOST_IOSTREAMS_WINDOWS //---------------------------------------------//
373
374file_descriptor::file_descriptor(int fd, file_descriptor_flags f)
375 : pimpl_(new impl_type)
376{ open(fd, f); }
377
378#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
379file_descriptor::file_descriptor(int fd, bool close_on_exit)
380 : pimpl_(new impl_type)
381{ open(fd, close_on_exit); }
382#endif
383
384#endif // #ifdef BOOST_IOSTREAMS_WINDOWS //-----------------------------------//
385
386file_descriptor::file_descriptor( const std::string& path,
387 BOOST_IOS::openmode mode )
388 : pimpl_(new impl_type)
389{ open(path, mode); }
390
391file_descriptor::file_descriptor( const char* path,
392 BOOST_IOS::openmode mode )
393 : pimpl_(new impl_type)
394{ open(path, mode); }
395
396file_descriptor::file_descriptor(const file_descriptor& other)
397 : pimpl_(other.pimpl_)
398 { }
399
400void file_descriptor::open(handle_type fd, file_descriptor_flags f)
401{ pimpl_->open(fd, static_cast<detail::file_descriptor_impl::flags>(f)); }
402
403#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
404void file_descriptor::open(handle_type fd, bool close_on_exit)
405{ pimpl_->open(fd, close_on_exit ?
406 detail::file_descriptor_impl::close_always :
407 detail::file_descriptor_impl::close_on_close); }
408#endif
409
410#ifdef BOOST_IOSTREAMS_WINDOWS //---------------------------------------------//
411
412void file_descriptor::open(int fd, file_descriptor_flags f)
413{ pimpl_->open(fd, static_cast<detail::file_descriptor_impl::flags>(f)); }
414
415#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
416void file_descriptor::open(int fd, bool close_on_exit)
417{ pimpl_->open(fd, close_on_exit ?
418 detail::file_descriptor_impl::close_always :
419 detail::file_descriptor_impl::close_on_close); }
420#endif
421
422#endif // #ifdef BOOST_IOSTREAMS_WINDOWS //-----------------------------------//
423
424void file_descriptor::open(const std::string& path, BOOST_IOS::openmode mode)
425{ open(detail::path(path), mode); }
426
427void file_descriptor::open(const char* path, BOOST_IOS::openmode mode)
428{ open(detail::path(path), mode); }
429
430bool file_descriptor::is_open() const { return pimpl_->is_open(); }
431
432void file_descriptor::close() { pimpl_->close(); }
433
434std::streamsize file_descriptor::read(char_type* s, std::streamsize n)
435{ return pimpl_->read(s, n); }
436
437std::streamsize file_descriptor::write(const char_type* s, std::streamsize n)
438{ return pimpl_->write(s, n); }
439
440std::streampos file_descriptor::seek(stream_offset off, BOOST_IOS::seekdir way)
441{ return pimpl_->seek(off, way); }
442
443detail::file_handle file_descriptor::handle() const { return pimpl_->handle_; }
444
445void file_descriptor::init() { pimpl_.reset(new impl_type); }
446
447void file_descriptor::open(
448 const detail::path& path,
449 BOOST_IOS::openmode mode,
450 BOOST_IOS::openmode base )
451{
452 mode |= base;
453 pimpl_->open(path, mode);
454}
455
456//------------------Implementation of file_descriptor_source------------------//
457
458file_descriptor_source::file_descriptor_source(
459 handle_type fd, file_descriptor_flags f)
460{ open(fd, f); }
461
462#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
463file_descriptor_source::file_descriptor_source(
464 handle_type fd, bool close_on_exit)
465{ open(fd, close_on_exit); }
466#endif
467
468#ifdef BOOST_IOSTREAMS_WINDOWS //---------------------------------------------//
469
470file_descriptor_source::file_descriptor_source(int fd, file_descriptor_flags f)
471{ open(fd, f); }
472
473#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
474file_descriptor_source::file_descriptor_source(int fd, bool close_on_exit)
475{ open(fd, close_on_exit); }
476#endif
477
478#endif // #ifdef BOOST_IOSTREAMS_WINDOWS //-----------------------------------//
479
480file_descriptor_source::file_descriptor_source(
481 const std::string& path, BOOST_IOS::openmode mode)
482{ open(path, mode); }
483
484file_descriptor_source::file_descriptor_source(
485 const char* path, BOOST_IOS::openmode mode)
486{ open(path, mode); }
487
488file_descriptor_source::file_descriptor_source(
489 const file_descriptor_source& other)
490 : file_descriptor(static_cast<const file_descriptor&>(other))
491 { }
492
493void file_descriptor_source::open(handle_type fd, file_descriptor_flags f)
494{ file_descriptor::open(fd, f); }
495
496#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
497void file_descriptor_source::open(handle_type fd, bool close_on_exit)
498{ file_descriptor::open(fd, close_on_exit); }
499#endif
500
501#ifdef BOOST_IOSTREAMS_WINDOWS //---------------------------------------------//
502
503void file_descriptor_source::open(int fd, file_descriptor_flags f)
504{ file_descriptor::open(fd, f); }
505
506#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
507void file_descriptor_source::open(int fd, bool close_on_exit)
508{ file_descriptor::open(fd, close_on_exit); }
509#endif
510
511#endif // #ifdef BOOST_IOSTREAMS_WINDOWS //-----------------------------------//
512
513void file_descriptor_source::open(
514 const std::string& path, BOOST_IOS::openmode mode)
515{ open(detail::path(path), mode); }
516
517void file_descriptor_source::open(
518 const char* path, BOOST_IOS::openmode mode)
519{ open(detail::path(path), mode); }
520
521void file_descriptor_source::open(
522 const detail::path& path, BOOST_IOS::openmode mode)
523{
524 if (mode & (BOOST_IOS::out | BOOST_IOS::app | BOOST_IOS::trunc))
525 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("invalid mode"));
526 file_descriptor::open(path, mode, BOOST_IOS::in);
527}
528
529//------------------Implementation of file_descriptor_sink--------------------//
530
531file_descriptor_sink::file_descriptor_sink(
532 handle_type fd, file_descriptor_flags f)
533{ open(fd, f); }
534
535#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
536file_descriptor_sink::file_descriptor_sink(
537 handle_type fd, bool close_on_exit)
538{ open(fd, close_on_exit); }
539#endif
540
541#ifdef BOOST_IOSTREAMS_WINDOWS //---------------------------------------------//
542
543file_descriptor_sink::file_descriptor_sink(int fd, file_descriptor_flags f)
544{ open(fd, f); }
545
546#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
547file_descriptor_sink::file_descriptor_sink(int fd, bool close_on_exit)
548{ open(fd, close_on_exit); }
549#endif
550
551#endif // #ifdef BOOST_IOSTREAMS_WINDOWS //-----------------------------------//
552
553file_descriptor_sink::file_descriptor_sink(
554 const std::string& path, BOOST_IOS::openmode mode)
555{ open(path, mode); }
556
557file_descriptor_sink::file_descriptor_sink(
558 const char* path, BOOST_IOS::openmode mode)
559{ open(path, mode); }
560
561file_descriptor_sink::file_descriptor_sink(const file_descriptor_sink& other)
562 : file_descriptor(static_cast<const file_descriptor&>(other))
563 { }
564
565void file_descriptor_sink::open(handle_type fd, file_descriptor_flags f)
566{ file_descriptor::open(fd, f); }
567
568#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
569void file_descriptor_sink::open(handle_type fd, bool close_on_exit)
570{ file_descriptor::open(fd, close_on_exit); }
571#endif
572
573#ifdef BOOST_IOSTREAMS_WINDOWS //---------------------------------------------//
574
575void file_descriptor_sink::open(int fd, file_descriptor_flags f)
576{ file_descriptor::open(fd, f); }
577
578#if defined(BOOST_IOSTREAMS_USE_DEPRECATED)
579void file_descriptor_sink::open(int fd, bool close_on_exit)
580{ file_descriptor::open(fd, close_on_exit); }
581#endif
582
583#endif // #ifdef BOOST_IOSTREAMS_WINDOWS //-----------------------------------//
584
585void file_descriptor_sink::open(
586 const std::string& path, BOOST_IOS::openmode mode)
587{ open(detail::path(path), mode); }
588
589void file_descriptor_sink::open(
590 const char* path, BOOST_IOS::openmode mode)
591{ open(detail::path(path), mode); }
592
593void file_descriptor_sink::open(
594 const detail::path& path, BOOST_IOS::openmode mode)
595{
596 if (mode & BOOST_IOS::in)
597 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("invalid mode"));
598 file_descriptor::open(path, mode, BOOST_IOS::out);
599}
600
601} } // End namespaces iostreams, boost.