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(error_code& ec)
110 : {
111 0 : auto const ev = native_close(fd_);
112 0 : if(ev)
113 0 : ec.assign(ev, system_category());
114 : else
115 0 : ec = {};
116 0 : }
117 :
118 : void
119 0 : file_posix::
120 : open(char const* path, file_mode mode, error_code& ec)
121 : {
122 0 : auto const ev = native_close(fd_);
123 0 : if(ev)
124 0 : ec.assign(ev, system_category());
125 : else
126 0 : ec = {};
127 :
128 0 : int f = 0;
129 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
130 0 : int advise = 0;
131 : #endif
132 0 : switch(mode)
133 : {
134 0 : default:
135 : case file_mode::read:
136 0 : f = O_RDONLY;
137 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
138 0 : advise = POSIX_FADV_RANDOM;
139 : #endif
140 0 : break;
141 0 : case file_mode::scan:
142 0 : f = O_RDONLY;
143 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
144 0 : advise = POSIX_FADV_SEQUENTIAL;
145 : #endif
146 0 : break;
147 :
148 0 : case file_mode::write:
149 0 : f = O_RDWR | O_CREAT | O_TRUNC;
150 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
151 0 : advise = POSIX_FADV_RANDOM;
152 : #endif
153 0 : break;
154 :
155 0 : case file_mode::write_new:
156 0 : f = O_RDWR | O_CREAT | O_EXCL;
157 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
158 0 : advise = POSIX_FADV_RANDOM;
159 : #endif
160 0 : break;
161 :
162 0 : case file_mode::write_existing:
163 0 : f = O_RDWR | O_EXCL;
164 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
165 0 : advise = POSIX_FADV_RANDOM;
166 : #endif
167 0 : break;
168 :
169 0 : case file_mode::append:
170 0 : f = O_WRONLY | O_CREAT | O_APPEND;
171 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
172 0 : advise = POSIX_FADV_SEQUENTIAL;
173 : #endif
174 0 : break;
175 :
176 0 : case file_mode::append_existing:
177 0 : f = O_WRONLY | O_APPEND;
178 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
179 0 : advise = POSIX_FADV_SEQUENTIAL;
180 : #endif
181 0 : break;
182 : }
183 : for(;;)
184 : {
185 0 : fd_ = ::open(path, f, 0644);
186 0 : if(fd_ != -1)
187 0 : break;
188 0 : auto const ev = errno;
189 0 : if(ev != EINTR)
190 : {
191 0 : ec.assign(ev, system_category());
192 0 : return;
193 : }
194 0 : }
195 : #if BOOST_HTTP_PROTO_USE_POSIX_FADVISE
196 0 : if(::posix_fadvise(fd_, 0, 0, advise))
197 : {
198 0 : auto const ev = errno;
199 0 : native_close(fd_);
200 0 : ec.assign(ev, system_category());
201 0 : return;
202 : }
203 : #endif
204 0 : ec = {};
205 : }
206 :
207 : std::uint64_t
208 0 : file_posix::
209 : size(error_code& ec) const
210 : {
211 0 : if(fd_ == -1)
212 : {
213 0 : ec = make_error_code(errc::bad_file_descriptor);
214 0 : return 0;
215 : }
216 : struct stat st;
217 0 : if(::fstat(fd_, &st) != 0)
218 : {
219 0 : ec.assign(errno, system_category());
220 0 : return 0;
221 : }
222 0 : ec = {};
223 0 : return st.st_size;
224 : }
225 :
226 : std::uint64_t
227 0 : file_posix::
228 : pos(error_code& ec) const
229 : {
230 0 : if(fd_ == -1)
231 : {
232 0 : ec = make_error_code(errc::bad_file_descriptor);
233 0 : return 0;
234 : }
235 0 : auto const result = ::lseek(fd_, 0, SEEK_CUR);
236 0 : if(result == (off_t)-1)
237 : {
238 0 : ec.assign(errno, system_category());
239 0 : return 0;
240 : }
241 0 : ec = {};
242 0 : return result;
243 : }
244 :
245 : void
246 0 : file_posix::
247 : seek(std::uint64_t offset, error_code& ec)
248 : {
249 0 : if(fd_ == -1)
250 : {
251 0 : ec = make_error_code(errc::bad_file_descriptor);
252 0 : return;
253 : }
254 0 : auto const result = ::lseek(fd_, offset, SEEK_SET);
255 0 : if(result == static_cast<off_t>(-1))
256 : {
257 0 : ec.assign(errno, system_category());
258 0 : return;
259 : }
260 0 : ec = {};
261 : }
262 :
263 : std::size_t
264 0 : file_posix::
265 : read(void* buffer, std::size_t n, error_code& ec) const
266 : {
267 0 : if(fd_ == -1)
268 : {
269 0 : ec = make_error_code(errc::bad_file_descriptor);
270 0 : return 0;
271 : }
272 0 : std::size_t nread = 0;
273 0 : while(n > 0)
274 : {
275 : // <limits> not required to define SSIZE_MAX so we avoid it
276 0 : constexpr auto ssmax =
277 : static_cast<std::size_t>((std::numeric_limits<
278 : decltype(::read(fd_, buffer, n))>::max)());
279 : auto const amount = (std::min)(
280 0 : n, ssmax);
281 0 : auto const result = ::read(fd_, buffer, amount);
282 0 : if(result == -1)
283 : {
284 0 : auto const ev = errno;
285 0 : if(ev == EINTR)
286 0 : continue;
287 0 : ec.assign(ev, system_category());
288 0 : return nread;
289 : }
290 0 : if(result == 0)
291 : {
292 : // short read
293 0 : return nread;
294 : }
295 0 : n -= result;
296 0 : nread += result;
297 0 : buffer = static_cast<char*>(buffer) + result;
298 : }
299 0 : return nread;
300 : }
301 :
302 : std::size_t
303 0 : file_posix::
304 : write(void const* buffer, std::size_t n, error_code& ec)
305 : {
306 0 : if(fd_ == -1)
307 : {
308 0 : ec = make_error_code(errc::bad_file_descriptor);
309 0 : return 0;
310 : }
311 0 : std::size_t nwritten = 0;
312 0 : while(n > 0)
313 : {
314 : // <limits> not required to define SSIZE_MAX so we avoid it
315 0 : constexpr auto ssmax =
316 : static_cast<std::size_t>((std::numeric_limits<
317 : decltype(::write(fd_, buffer, n))>::max)());
318 : auto const amount = (std::min)(
319 0 : n, ssmax);
320 0 : auto const result = ::write(fd_, buffer, amount);
321 0 : if(result == -1)
322 : {
323 0 : auto const ev = errno;
324 0 : if(ev == EINTR)
325 0 : continue;
326 0 : ec.assign(ev, system_category());
327 0 : return nwritten;
328 : }
329 0 : n -= result;
330 0 : nwritten += result;
331 0 : buffer = static_cast<char const*>(buffer) + result;
332 : }
333 0 : return nwritten;
334 : }
335 :
336 : } // http_proto
337 : } // boost
338 :
339 : #endif
340 :
341 : #endif
|