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