LCOV - code coverage report
Current view: top level - libs/http_proto/src - file_posix.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 125 150 83.3 %
Date: 2024-01-17 18:52:26 Functions: 12 12 100.0 %

          Line data    Source code
       1             : //
       2             : // Copyright (c) 2022 Vinnie Falco (vinnie.falco@gmail.com)
       3             : //
       4             : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5             : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6             : //
       7             : // Official repository: https://github.com/cppalliance/http_proto
       8             : //
       9             : 
      10             : #include <boost/http_proto/file_posix.hpp>
      11             : 
      12             : #if BOOST_HTTP_PROTO_USE_POSIX_FILE
      13             : 
      14             : #include <boost/core/exchange.hpp>
      15             : #include <limits>
      16             : #include <fcntl.h>
      17             : #include <sys/types.h>
      18             : #include <sys/uio.h>
      19             : #include <sys/stat.h>
      20             : #include <unistd.h>
      21             : #include <limits.h>
      22             : 
      23             : #if ! defined(BOOST_HTTP_PROTO_NO_POSIX_FADVISE)
      24             : # if defined(__APPLE__) || (defined(__ANDROID__) && (__ANDROID_API__ < 21))
      25             : #  define BOOST_HTTP_PROTO_NO_POSIX_FADVISE
      26             : # endif
      27             : #endif
      28             : 
      29             : #if ! defined(BOOST_HTTP_PROTO_USE_POSIX_FADVISE)
      30             : # if ! defined(BOOST_HTTP_PROTO_NO_POSIX_FADVISE)
      31             : #  define BOOST_HTTP_PROTO_USE_POSIX_FADVISE 1
      32             : # else
      33             : #  define BOOST_HTTP_PROTO_USE_POSIX_FADVISE 0
      34             : # endif
      35             : #endif
      36             : 
      37             : namespace boost {
      38             : namespace http_proto {
      39             : 
      40             : int
      41          51 : file_posix::
      42             : native_close(native_handle_type& fd)
      43             : {
      44             : /*  https://github.com/boostorg/beast/issues/1445
      45             : 
      46             :     This function is tuned for Linux / Mac OS:
      47             : 
      48             :     * only calls close() once
      49             :     * returns the error directly to the caller
      50             :     * does not loop on EINTR
      51             : 
      52             :     If this is incorrect for the platform, then the
      53             :     caller will need to implement their own type
      54             :     meeting the File requirements and use the correct
      55             :     behavior.
      56             : 
      57             :     See:
      58             :         http://man7.org/linux/man-pages/man2/close.2.html
      59             : */
      60          51 :     int ev = 0;
      61          51 :     if(fd != -1)
      62             :     {
      63          18 :         if(::close(fd) != 0)
      64           0 :             ev = errno;
      65          18 :         fd = -1;
      66             :     }
      67          51 :     return ev;
      68             : }
      69             : 
      70          23 : file_posix::
      71          23 : ~file_posix()
      72             : {
      73          23 :     native_close(fd_);
      74          23 : }
      75             : 
      76           1 : file_posix::
      77             : file_posix(
      78           1 :     file_posix&& other) noexcept
      79           1 :     : fd_(boost::exchange(other.fd_, -1))
      80             : {
      81           1 : }
      82             : 
      83             : file_posix&
      84           3 : file_posix::
      85             : operator=(
      86             :     file_posix&& other) noexcept
      87             : {
      88           3 :     if(&other == this)
      89           1 :         return *this;
      90           2 :     native_close(fd_);
      91           2 :     fd_ = other.fd_;
      92           2 :     other.fd_ = -1;
      93           2 :     return *this;
      94             : }
      95             : 
      96             : void
      97           1 : file_posix::
      98             : native_handle(native_handle_type fd)
      99             : {
     100           1 :     native_close(fd_);
     101           1 :     fd_ = fd;
     102           1 : }
     103             : 
     104             : void
     105           4 : file_posix::
     106             : close(
     107             :     system::error_code& ec)
     108             : {
     109           4 :     auto const ev = native_close(fd_);
     110           4 :     if(ev)
     111           0 :         ec.assign(ev,
     112             :             system::system_category());
     113             :     else
     114           4 :         ec = {};
     115           4 : }
     116             : 
     117             : void
     118          21 : file_posix::
     119             : open(char const* path, file_mode mode, system::error_code& ec)
     120             : {
     121          21 :     auto const ev = native_close(fd_);
     122          21 :     if(ev)
     123           0 :         ec.assign(ev,
     124             :             system::system_category());
     125             :     else
     126          21 :         ec = {};
     127             : 
     128          21 :     int f = 0;
     129             : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
     130          21 :     int advise = 0;
     131             : #endif
     132          21 :     switch(mode)
     133             :     {
     134           2 :     default:
     135             :     case file_mode::read:
     136           2 :         f = O_RDONLY;
     137             :     #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
     138           2 :         advise = POSIX_FADV_RANDOM;
     139             :     #endif
     140           2 :         break;
     141           1 :     case file_mode::scan:
     142           1 :         f = O_RDONLY;
     143             :     #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
     144           1 :         advise = POSIX_FADV_SEQUENTIAL;
     145             :     #endif
     146           1 :         break;
     147             : 
     148          10 :     case file_mode::write:
     149          10 :         f = O_RDWR | O_CREAT | O_TRUNC;
     150             :     #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
     151          10 :         advise = POSIX_FADV_RANDOM;
     152             :     #endif
     153          10 :         break;
     154             : 
     155           2 :     case file_mode::write_new:
     156           2 :         f = O_RDWR | O_CREAT | O_EXCL;
     157             :     #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
     158           2 :         advise = POSIX_FADV_RANDOM;
     159             :     #endif
     160           2 :         break;
     161             : 
     162           2 :     case file_mode::write_existing:
     163           2 :         f = O_RDWR | O_EXCL;
     164             :     #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
     165           2 :         advise = POSIX_FADV_RANDOM;
     166             :     #endif
     167           2 :         break;
     168             : 
     169           2 :     case file_mode::append:
     170           2 :         f = O_WRONLY | O_CREAT | O_APPEND;
     171             :     #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
     172           2 :         advise = POSIX_FADV_SEQUENTIAL;
     173             :     #endif
     174           2 :         break;
     175             : 
     176           2 :     case file_mode::append_existing:
     177           2 :         f = O_WRONLY | O_APPEND;
     178             :     #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
     179           2 :         advise = POSIX_FADV_SEQUENTIAL;
     180             :     #endif
     181           2 :         break;
     182             :     }
     183             :     for(;;)
     184             :     {
     185          21 :         fd_ = ::open(path, f, 0644);
     186          21 :         if(fd_ != -1)
     187          18 :             break;
     188           3 :         auto const ev = errno;
     189           3 :         if(ev != EINTR)
     190             :         {
     191           3 :             ec.assign(ev,
     192             :                 system::system_category());
     193           3 :             return;
     194             :         }
     195           0 :     }
     196             : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
     197          18 :     if(::posix_fadvise(fd_, 0, 0, advise))
     198             :     {
     199           0 :         auto const ev = errno;
     200           0 :         native_close(fd_);
     201           0 :         ec.assign(ev,
     202             :             system::system_category());
     203           0 :         return;
     204             :     }
     205             : #endif
     206          18 :     ec = {};
     207             : }
     208             : 
     209             : std::uint64_t
     210           2 : file_posix::
     211             : size(
     212             :     system::error_code& ec) const
     213             : {
     214           2 :     if(fd_ == -1)
     215             :     {
     216             :         ec = make_error_code(
     217           1 :             system::errc::bad_file_descriptor);
     218           1 :         return 0;
     219             :     }
     220             :     struct stat st;
     221           1 :     if(::fstat(fd_, &st) != 0)
     222             :     {
     223           0 :         ec.assign(errno,
     224             :             system::system_category());
     225           0 :         return 0;
     226             :     }
     227           1 :     ec = {};
     228           1 :     return st.st_size;
     229             : }
     230             : 
     231             : std::uint64_t
     232           3 : file_posix::
     233             : pos(
     234             :     system::error_code& ec) const
     235             : {
     236           3 :     if(fd_ == -1)
     237             :     {
     238             :         ec = make_error_code(
     239           1 :             system::errc::bad_file_descriptor);
     240           1 :         return 0;
     241             :     }
     242           2 :     auto const result = ::lseek(fd_, 0, SEEK_CUR);
     243           2 :     if(result == (::off_t)-1)
     244             :     {
     245           0 :         ec.assign(errno,
     246             :             system::system_category());
     247           0 :         return 0;
     248             :     }
     249           2 :     ec = {};
     250           2 :     return result;
     251             : }
     252             : 
     253             : void
     254           2 : file_posix::
     255             : seek(std::uint64_t offset,
     256             :     system::error_code& ec)
     257             : {
     258           2 :     if(fd_ == -1)
     259             :     {
     260             :         ec = make_error_code(
     261           1 :             system::errc::bad_file_descriptor);
     262           1 :         return;
     263             :     }
     264           1 :     auto const result = ::lseek(fd_, offset, SEEK_SET);
     265           1 :     if(result == static_cast<::off_t>(-1))
     266             :     {
     267           0 :         ec.assign(errno,
     268             :             system::system_category());
     269           0 :         return;
     270             :     }
     271           1 :     ec = {};
     272             : }
     273             : 
     274             : std::size_t
     275           3 : file_posix::
     276             : read(void* buffer, std::size_t n,
     277             :     system::error_code& ec) const
     278             : {
     279           3 :     if(fd_ == -1)
     280             :     {
     281             :         ec = make_error_code(
     282           1 :             system::errc::bad_file_descriptor);
     283           1 :         return 0;
     284             :     }
     285           2 :     std::size_t nread = 0;
     286           4 :     while(n > 0)
     287             :     {
     288             :         // <limits> not required to define SSIZE_MAX so we avoid it
     289           2 :         constexpr auto ssmax =
     290             :             static_cast<std::size_t>((std::numeric_limits<
     291             :                 decltype(::read(fd_, buffer, n))>::max)());
     292             :         auto const amount = (std::min)(
     293           2 :             n, ssmax);
     294           2 :         auto const result = ::read(fd_, buffer, amount);
     295           2 :         if(result == -1)
     296             :         {
     297           0 :             auto const ev = errno;
     298           0 :             if(ev == EINTR)
     299           0 :                 continue;
     300           0 :             ec.assign(ev,
     301             :                 system::system_category());
     302           0 :             return nread;
     303             :         }
     304           2 :         if(result == 0)
     305             :         {
     306             :             // short read
     307           0 :             return nread;
     308             :         }
     309           2 :         n -= result;
     310           2 :         nread += result;
     311           2 :         buffer = static_cast<char*>(buffer) + result;
     312             :     }
     313           2 :     return nread;
     314             : }
     315             : 
     316             : std::size_t
     317           5 : file_posix::
     318             : write(void const* buffer, std::size_t n,
     319             :     system::error_code& ec)
     320             : {
     321           5 :     if(fd_ == -1)
     322             :     {
     323             :         ec = make_error_code(
     324           1 :             system::errc::bad_file_descriptor);
     325           1 :         return 0;
     326             :     }
     327           4 :     std::size_t nwritten = 0;
     328           8 :     while(n > 0)
     329             :     {
     330             :         // <limits> not required to define SSIZE_MAX so we avoid it
     331           4 :         constexpr auto ssmax =
     332             :             static_cast<std::size_t>((std::numeric_limits<
     333             :                 decltype(::write(fd_, buffer, n))>::max)());
     334             :         auto const amount = (std::min)(
     335           4 :             n, ssmax);
     336           4 :         auto const result = ::write(fd_, buffer, amount);
     337           4 :         if(result == -1)
     338             :         {
     339           0 :             auto const ev = errno;
     340           0 :             if(ev == EINTR)
     341           0 :                 continue;
     342           0 :             ec.assign(ev,
     343             :                 system::system_category());
     344           0 :             return nwritten;
     345             :         }
     346           4 :         n -= result;
     347           4 :         nwritten += result;
     348           4 :         buffer = static_cast<char const*>(buffer) + result;
     349             :     }
     350           4 :     return nwritten;
     351             : }
     352             : 
     353             : } // http_proto
     354             : } // boost
     355             : 
     356             : #endif

Generated by: LCOV version 1.15