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
|