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