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

Generated by: LCOV version 2.3