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
|