LCOV - code coverage report
Current view: top level - corosio/native/detail - validate_fd.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 96.0 % 50 48 2
Test Date: 2026-10-08 17:58:26 Functions: 100.0 % 4 4

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Steve Gerbino
       3                 : // Copyright (c) 2026 Michael Vandeberg
       4                 : //
       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                 : // Official repository: https://github.com/cppalliance/corosio
       9                 : //
      10                 : 
      11                 : #ifndef BOOST_COROSIO_NATIVE_DETAIL_VALIDATE_FD_HPP
      12                 : #define BOOST_COROSIO_NATIVE_DETAIL_VALIDATE_FD_HPP
      13                 : 
      14                 : #include <boost/corosio/detail/platform.hpp>
      15                 : 
      16                 : #if BOOST_COROSIO_POSIX
      17                 : 
      18                 : #include <boost/corosio/native/detail/make_err.hpp>
      19                 : #include <boost/corosio/native/detail/posix/large_file.hpp>
      20                 : 
      21                 : #include <cerrno>
      22                 : #include <system_error>
      23                 : 
      24                 : #include <fcntl.h>
      25                 : #include <sys/socket.h>
      26                 : #include <sys/stat.h>
      27                 : 
      28                 : namespace boost::corosio::detail {
      29                 : 
      30                 : /** Validate a caller-supplied socket fd for adoption.
      31                 : 
      32                 :     Non-mutating: interrogates the fd without changing any of its
      33                 :     flags, so a rejected fd goes back to the caller untouched.
      34                 : 
      35                 :     @param fd The descriptor to validate.
      36                 :     @param expected_type `SOCK_STREAM` or `SOCK_DGRAM`.
      37                 :     @param is_ip Accept `AF_INET`/`AF_INET6` when true, `AF_UNIX`
      38                 :         when false.
      39                 :     @return Empty on success; `EBADF`, `EAFNOSUPPORT`, `EPROTOTYPE`,
      40                 :         or the `errno` reported by the interrogating call.
      41                 : */
      42                 : inline std::error_code
      43 HIT         371 : validate_socket_fd(int fd, int expected_type, bool is_ip) noexcept
      44                 : {
      45             371 :     if (fd < 0)
      46              14 :         return make_err(EBADF);
      47                 : 
      48             357 :     sockaddr_storage st{};
      49             357 :     socklen_t st_len = sizeof(st);
      50             357 :     if (::getsockname(fd, reinterpret_cast<sockaddr*>(&st), &st_len) != 0)
      51               5 :         return make_err(errno);
      52             352 :     if (is_ip)
      53                 :     {
      54              39 :         if (st.ss_family != AF_INET && st.ss_family != AF_INET6)
      55               6 :             return make_err(EAFNOSUPPORT);
      56                 :     }
      57             313 :     else if (st.ss_family != AF_UNIX)
      58                 :     {
      59               2 :         return make_err(EAFNOSUPPORT);
      60                 :     }
      61                 : 
      62             344 :     int sock_type     = 0;
      63             344 :     socklen_t opt_len = sizeof(sock_type);
      64             344 :     if (::getsockopt(fd, SOL_SOCKET, SO_TYPE, &sock_type, &opt_len) != 0)
      65              15 :         return make_err(errno);
      66             329 :     if (sock_type != expected_type)
      67              14 :         return make_err(EPROTOTYPE);
      68                 : 
      69             315 :     return {};
      70                 : }
      71                 : 
      72                 : /** Validate a caller-supplied fd for adoption by @ref posix_stream_descriptor.
      73                 : 
      74                 :     Non-mutating: interrogates the fd without changing any of its
      75                 :     flags, so a rejected fd goes back to the caller untouched. In
      76                 :     particular `O_NONBLOCK` is not applied here — see
      77                 :     @ref ensure_nonblocking.
      78                 : 
      79                 :     The file-type test is a reject-list, not an accept-list. The
      80                 :     flagship descriptor kinds -- eventfd, timerfd, inotify, pidfd --
      81                 :     are anonymous inodes whose `st_mode` type bits are all zero, so
      82                 :     an accept-list would silently reject exactly the fds this type
      83                 :     exists to carry.
      84                 : 
      85                 :     @param fd The descriptor to validate.
      86                 :     @return Empty on success; `EBADF` for a closed or negative fd,
      87                 :         `operation_not_supported` for a regular file, directory or
      88                 :         block device, or the `errno` reported by `fstat`.
      89                 : */
      90                 : inline std::error_code
      91             120 : validate_descriptor_fd(int fd) noexcept
      92                 : {
      93             120 :     if (fd < 0)
      94               3 :         return make_err(EBADF);
      95                 : 
      96             117 :     file_stat_t st{};
      97             117 :     if (file_fstat(fd, &st) != 0)
      98               1 :         return make_err(errno);
      99                 : 
     100                 :     // Regular files, block devices and directories are the province of
     101                 :     // stream_file / random_access_file, whose assign() already adopts
     102                 :     // them; a reactor cannot report readiness for them anyway.
     103             116 :     switch (st.st_mode & S_IFMT)
     104                 :     {
     105               4 :     case S_IFREG:
     106                 :     case S_IFBLK:
     107                 :     case S_IFDIR:
     108               4 :         return std::make_error_code(std::errc::operation_not_supported);
     109             112 :     default:
     110             112 :         return {};
     111                 :     }
     112                 : }
     113                 : 
     114                 : /** Validate a caller-supplied fd for adoption by a file object.
     115                 : 
     116                 :     Non-mutating. Accepts the kinds a file object can position and
     117                 :     read: regular files, block devices, and character devices such
     118                 :     as /dev/null and /dev/zero.
     119                 : 
     120                 :     This is an accept-list, the inverse of @ref validate_descriptor_fd's
     121                 :     reject-list: a file object needs a positionable fd, and the
     122                 :     anonymous inodes that motivate the descriptor reject-list are
     123                 :     exactly what a file object cannot use.
     124                 : 
     125                 :     `S_IFCHR` is deliberately broad: it admits `/dev/null` and
     126                 :     `/dev/zero`, but also non-seekable character devices such as a
     127                 :     tty. Those pass here and then fail loudly at first I/O on the
     128                 :     POSIX backends, where `preadv`/`pwritev` report `ESPIPE`.
     129                 : 
     130                 :     @param fd The descriptor to validate.
     131                 :     @return Empty on success; `EBADF` for a closed or negative fd,
     132                 :         `operation_not_supported` for a directory or a descriptor
     133                 :         with no file position, or the `errno` from `fstat`.
     134                 : */
     135                 : inline std::error_code
     136             420 : validate_file_fd(int fd) noexcept
     137                 : {
     138             420 :     if (fd < 0)
     139               4 :         return make_err(EBADF);
     140                 : 
     141             416 :     file_stat_t st{};
     142             416 :     if (file_fstat(fd, &st) != 0)
     143 MIS           0 :         return make_err(errno);
     144                 : 
     145 HIT         416 :     switch (st.st_mode & S_IFMT)
     146                 :     {
     147             411 :     case S_IFREG:
     148                 :     case S_IFBLK:
     149                 :     case S_IFCHR:
     150             411 :         return {};
     151               5 :     default:
     152               5 :         return std::make_error_code(std::errc::operation_not_supported);
     153                 :     }
     154                 : }
     155                 : 
     156                 : /** Put a descriptor into non-blocking mode, idempotently.
     157                 : 
     158                 :     The reactor backends call it lazily on the first `read_some` /
     159                 :     `write_some`, never from `assign()`; io_uring never calls it. The
     160                 :     change is permanent: `O_NONBLOCK` lives on the
     161                 :     shared open file description, so restoring it later would race
     162                 :     every other holder of that description. A `wait()`-only user
     163                 :     never reaches this function and their fd is never modified.
     164                 : 
     165                 :     @param fd The descriptor to modify.
     166                 :     @return Empty on success, otherwise the `errno` from `fcntl`.
     167                 : */
     168                 : inline std::error_code
     169              64 : ensure_nonblocking(int fd) noexcept
     170                 : {
     171              64 :     int flags = ::fcntl(fd, F_GETFL, 0);
     172              64 :     if (flags < 0)
     173               1 :         return make_err(errno);
     174              63 :     if (flags & O_NONBLOCK)
     175               9 :         return {};
     176              54 :     if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
     177 MIS           0 :         return make_err(errno);
     178 HIT          54 :     return {};
     179                 : }
     180                 : 
     181                 : } // namespace boost::corosio::detail
     182                 : 
     183                 : #endif // BOOST_COROSIO_POSIX
     184                 : 
     185                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_VALIDATE_FD_HPP
        

Generated by: LCOV version 2.3