// // Copyright (c) 2015-2019 Vinnie Falco (vinnie dot falco at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/beast // #ifndef BOOST_BEAST_CORE_IMPL_FILE_POSIX_IPP #define BOOST_BEAST_CORE_IMPL_FILE_POSIX_IPP #include #if BOOST_BEAST_USE_POSIX_FILE #include #include #include #include #include #include #include #include #if ! defined(BOOST_BEAST_NO_POSIX_FADVISE) # if defined(__APPLE__) || (defined(__ANDROID__) && (__ANDROID_API__ < 21)) # define BOOST_BEAST_NO_POSIX_FADVISE # endif #endif #if ! defined(BOOST_BEAST_USE_POSIX_FADVISE) # if ! defined(BOOST_BEAST_NO_POSIX_FADVISE) # define BOOST_BEAST_USE_POSIX_FADVISE 1 # else # define BOOST_BEAST_USE_POSIX_FADVISE 0 # endif #endif namespace boost { namespace beast { int file_posix:: native_close(native_handle_type& fd) { /* https://github.com/boostorg/beast/issues/1445 This function is tuned for Linux / Mac OS: * only calls close() once * returns the error directly to the caller * does not loop on EINTR If this is incorrect for the platform, then the caller will need to implement their own type meeting the File requirements and use the correct behavior. See: http://man7.org/linux/man-pages/man2/close.2.html */ int ev = 0; if(fd != -1) { if(::close(fd) != 0) ev = errno; fd = -1; } return ev; } file_posix:: ~file_posix() { native_close(fd_); } file_posix:: file_posix(file_posix&& other) : fd_(boost::exchange(other.fd_, -1)) { } file_posix& file_posix:: operator=(file_posix&& other) { if(&other == this) return *this; native_close(fd_); fd_ = other.fd_; other.fd_ = -1; return *this; } void file_posix:: native_handle(native_handle_type fd) { native_close(fd_); fd_ = fd; } void file_posix:: close(error_code& ec) { auto const ev = native_close(fd_); if(ev) ec.assign(ev, system_category()); else ec = {}; } void file_posix:: open(char const* path, file_mode mode, error_code& ec) { auto const ev = native_close(fd_); if(ev) ec.assign(ev, system_category()); else ec = {}; int f = 0; #if BOOST_BEAST_USE_POSIX_FADVISE int advise = 0; #endif switch(mode) { default: case file_mode::read: f = O_RDONLY; #if BOOST_BEAST_USE_POSIX_FADVISE advise = POSIX_FADV_RANDOM; #endif break; case file_mode::scan: f = O_RDONLY; #if BOOST_BEAST_USE_POSIX_FADVISE advise = POSIX_FADV_SEQUENTIAL; #endif break; case file_mode::write: f = O_RDWR | O_CREAT | O_TRUNC; #if BOOST_BEAST_USE_POSIX_FADVISE advise = POSIX_FADV_RANDOM; #endif break; case file_mode::write_new: f = O_RDWR | O_CREAT | O_EXCL; #if BOOST_BEAST_USE_POSIX_FADVISE advise = POSIX_FADV_RANDOM; #endif break; case file_mode::write_existing: f = O_RDWR | O_EXCL; #if BOOST_BEAST_USE_POSIX_FADVISE advise = POSIX_FADV_RANDOM; #endif break; case file_mode::append: f = O_WRONLY | O_CREAT | O_APPEND; #if BOOST_BEAST_USE_POSIX_FADVISE advise = POSIX_FADV_SEQUENTIAL; #endif break; case file_mode::append_existing: f = O_WRONLY | O_APPEND; #if BOOST_BEAST_USE_POSIX_FADVISE advise = POSIX_FADV_SEQUENTIAL; #endif break; } for(;;) { fd_ = ::open(path, f, 0644); if(fd_ != -1) break; auto const ev = errno; if(ev != EINTR) { ec.assign(ev, system_category()); return; } } #if BOOST_BEAST_USE_POSIX_FADVISE if(::posix_fadvise(fd_, 0, 0, advise)) { auto const ev = errno; native_close(fd_); ec.assign(ev, system_category()); return; } #endif ec = {}; } std::uint64_t file_posix:: size(error_code& ec) const { if(fd_ == -1) { ec = make_error_code(errc::bad_file_descriptor); return 0; } struct stat st; if(::fstat(fd_, &st) != 0) { ec.assign(errno, system_category()); return 0; } ec = {}; return st.st_size; } std::uint64_t file_posix:: pos(error_code& ec) const { if(fd_ == -1) { ec = make_error_code(errc::bad_file_descriptor); return 0; } auto const result = ::lseek(fd_, 0, SEEK_CUR); if(result == (off_t)-1) { ec.assign(errno, system_category()); return 0; } ec = {}; return result; } void file_posix:: seek(std::uint64_t offset, error_code& ec) { if(fd_ == -1) { ec = make_error_code(errc::bad_file_descriptor); return; } auto const result = ::lseek(fd_, offset, SEEK_SET); if(result == static_cast(-1)) { ec.assign(errno, system_category()); return; } ec = {}; } std::size_t file_posix:: read(void* buffer, std::size_t n, error_code& ec) const { if(fd_ == -1) { ec = make_error_code(errc::bad_file_descriptor); return 0; } std::size_t nread = 0; while(n > 0) { // not required to define SSIZE_MAX so we avoid it constexpr auto ssmax = static_cast((std::numeric_limits< decltype(::read(fd_, buffer, n))>::max)()); auto const amount = (std::min)( n, ssmax); auto const result = ::read(fd_, buffer, amount); if(result == -1) { auto const ev = errno; if(ev == EINTR) continue; ec.assign(ev, system_category()); return nread; } if(result == 0) { // short read return nread; } n -= result; nread += result; buffer = static_cast(buffer) + result; } return nread; } std::size_t file_posix:: write(void const* buffer, std::size_t n, error_code& ec) { if(fd_ == -1) { ec = make_error_code(errc::bad_file_descriptor); return 0; } std::size_t nwritten = 0; while(n > 0) { // not required to define SSIZE_MAX so we avoid it constexpr auto ssmax = static_cast((std::numeric_limits< decltype(::write(fd_, buffer, n))>::max)()); auto const amount = (std::min)( n, ssmax); auto const result = ::write(fd_, buffer, amount); if(result == -1) { auto const ev = errno; if(ev == EINTR) continue; ec.assign(ev, system_category()); return nwritten; } n -= result; nwritten += result; buffer = static_cast(buffer) + result; } return nwritten; } } // beast } // boost #endif #endif