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 0 : 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 0 : int ev = 0;
64 0 : if(fd != -1)
65 : {
66 0 : if(::close(fd) != 0)
67 0 : ev = errno;
68 0 : fd = -1;
69 : }
70 0 : return ev;
71 : }
72 :
73 0 : file_posix::
74 0 : ~file_posix()
75 : {
76 0 : native_close(fd_);
77 0 : }
78 :
79 0 : file_posix::
80 : file_posix(
81 0 : file_posix&& other) noexcept
82 0 : : fd_(boost::exchange(other.fd_, -1))
83 : {
84 0 : }
85 :
86 : file_posix&
87 0 : file_posix::
88 : operator=(
89 : file_posix&& other) noexcept
90 : {
91 0 : if(&other == this)
92 0 : return *this;
93 0 : native_close(fd_);
94 0 : fd_ = other.fd_;
95 0 : other.fd_ = -1;
96 0 : return *this;
97 : }
98 :
99 : void
100 0 : file_posix::
101 : native_handle(native_handle_type fd)
102 : {
103 0 : native_close(fd_);
104 0 : fd_ = fd;
105 0 : }
106 :
107 : void
108 0 : file_posix::
109 : close(
110 : system::error_code& ec)
111 : {
112 0 : auto const ev = native_close(fd_);
113 0 : if(ev)
114 0 : ec.assign(ev,
115 : system::system_category());
116 : else
117 0 : ec = {};
118 0 : }
119 :
120 : void
121 0 : file_posix::
122 : open(char const* path, file_mode mode, system::error_code& ec)
123 : {
124 0 : auto const ev = native_close(fd_);
125 0 : if(ev)
126 0 : ec.assign(ev,
127 : system::system_category());
128 : else
129 0 : ec = {};
130 :
131 0 : int f = 0;
132 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
133 0 : int advise = 0;
134 : #endif
135 0 : switch(mode)
136 : {
137 0 : default:
138 : case file_mode::read:
139 0 : f = O_RDONLY;
140 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
141 0 : advise = POSIX_FADV_RANDOM;
142 : #endif
143 0 : break;
144 0 : case file_mode::scan:
145 0 : f = O_RDONLY;
146 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
147 0 : advise = POSIX_FADV_SEQUENTIAL;
148 : #endif
149 0 : break;
150 :
151 0 : case file_mode::write:
152 0 : f = O_RDWR | O_CREAT | O_TRUNC;
153 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
154 0 : advise = POSIX_FADV_RANDOM;
155 : #endif
156 0 : break;
157 :
158 0 : case file_mode::write_new:
159 0 : f = O_RDWR | O_CREAT | O_EXCL;
160 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
161 0 : advise = POSIX_FADV_RANDOM;
162 : #endif
163 0 : break;
164 :
165 0 : case file_mode::write_existing:
166 0 : f = O_RDWR | O_EXCL;
167 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
168 0 : advise = POSIX_FADV_RANDOM;
169 : #endif
170 0 : break;
171 :
172 0 : case file_mode::append:
173 0 : f = O_WRONLY | O_CREAT | O_APPEND;
174 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
175 0 : advise = POSIX_FADV_SEQUENTIAL;
176 : #endif
177 0 : break;
178 :
179 0 : case file_mode::append_existing:
180 0 : f = O_WRONLY | O_APPEND;
181 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
182 0 : advise = POSIX_FADV_SEQUENTIAL;
183 : #endif
184 0 : break;
185 : }
186 : for(;;)
187 : {
188 0 : fd_ = ::open(path, f, 0644);
189 0 : if(fd_ != -1)
190 0 : break;
191 0 : auto const ev = errno;
192 0 : if(ev != EINTR)
193 : {
194 0 : ec.assign(ev,
195 : system::system_category());
196 0 : return;
197 : }
198 0 : }
199 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
200 0 : 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 0 : ec = {};
210 : }
211 :
212 : std::uint64_t
213 0 : file_posix::
214 : size(
215 : system::error_code& ec) const
216 : {
217 0 : if(fd_ == -1)
218 : {
219 : ec = make_error_code(
220 0 : system::errc::bad_file_descriptor);
221 0 : return 0;
222 : }
223 : struct stat st;
224 0 : if(::fstat(fd_, &st) != 0)
225 : {
226 0 : ec.assign(errno,
227 : system::system_category());
228 0 : return 0;
229 : }
230 0 : ec = {};
231 0 : return st.st_size;
232 : }
233 :
234 : std::uint64_t
235 0 : file_posix::
236 : pos(
237 : system::error_code& ec) const
238 : {
239 0 : if(fd_ == -1)
240 : {
241 : ec = make_error_code(
242 0 : system::errc::bad_file_descriptor);
243 0 : return 0;
244 : }
245 0 : auto const result = ::lseek(fd_, 0, SEEK_CUR);
246 0 : if(result == (off_t)-1)
247 : {
248 0 : ec.assign(errno,
249 : system::system_category());
250 0 : return 0;
251 : }
252 0 : ec = {};
253 0 : return result;
254 : }
255 :
256 : void
257 0 : file_posix::
258 : seek(std::uint64_t offset,
259 : system::error_code& ec)
260 : {
261 0 : if(fd_ == -1)
262 : {
263 : ec = make_error_code(
264 0 : system::errc::bad_file_descriptor);
265 0 : return;
266 : }
267 0 : auto const result = ::lseek(fd_, offset, SEEK_SET);
268 0 : if(result == static_cast<off_t>(-1))
269 : {
270 0 : ec.assign(errno,
271 : system::system_category());
272 0 : return;
273 : }
274 0 : ec = {};
275 : }
276 :
277 : std::size_t
278 0 : file_posix::
279 : read(void* buffer, std::size_t n,
280 : system::error_code& ec) const
281 : {
282 0 : if(fd_ == -1)
283 : {
284 : ec = make_error_code(
285 0 : system::errc::bad_file_descriptor);
286 0 : return 0;
287 : }
288 0 : std::size_t nread = 0;
289 0 : while(n > 0)
290 : {
291 : // <limits> not required to define SSIZE_MAX so we avoid it
292 0 : 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 0 : n, ssmax);
297 0 : auto const result = ::read(fd_, buffer, amount);
298 0 : 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 0 : if(result == 0)
308 : {
309 : // short read
310 0 : return nread;
311 : }
312 0 : n -= result;
313 0 : nread += result;
314 0 : buffer = static_cast<char*>(buffer) + result;
315 : }
316 0 : return nread;
317 : }
318 :
319 : std::size_t
320 0 : file_posix::
321 : write(void const* buffer, std::size_t n,
322 : system::error_code& ec)
323 : {
324 0 : if(fd_ == -1)
325 : {
326 : ec = make_error_code(
327 0 : system::errc::bad_file_descriptor);
328 0 : return 0;
329 : }
330 0 : std::size_t nwritten = 0;
331 0 : while(n > 0)
332 : {
333 : // <limits> not required to define SSIZE_MAX so we avoid it
334 0 : 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 0 : n, ssmax);
339 0 : auto const result = ::write(fd_, buffer, amount);
340 0 : 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 0 : n -= result;
350 0 : nwritten += result;
351 0 : buffer = static_cast<char const*>(buffer) + result;
352 : }
353 0 : return nwritten;
354 : }
355 :
356 : } // http_proto
357 : } // boost
358 :
359 : #endif
360 :
361 : #endif
|