src/detail/file_posix.cpp

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