1  
//
1  
//
2  
// Copyright (c) 2022 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2022 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/http
7  
// Official repository: https://github.com/cppalliance/http
8  
//
8  
//
9  

9  

10  
#include <boost/http/detail/file_posix.hpp>
10  
#include <boost/http/detail/file_posix.hpp>
11  

11  

12  
#if BOOST_HTTP_USE_POSIX_FILE
12  
#if BOOST_HTTP_USE_POSIX_FILE
13  

13  

14  
#include <boost/core/exchange.hpp>
14  
#include <boost/core/exchange.hpp>
15  
#include <limits>
15  
#include <limits>
16  
#include <fcntl.h>
16  
#include <fcntl.h>
17  
#include <sys/types.h>
17  
#include <sys/types.h>
18  
#include <sys/uio.h>
18  
#include <sys/uio.h>
19  
#include <sys/stat.h>
19  
#include <sys/stat.h>
20  
#include <unistd.h>
20  
#include <unistd.h>
21  
#include <limits.h>
21  
#include <limits.h>
22  

22  

23  
#if ! defined(BOOST_HTTP_NO_POSIX_FADVISE)
23  
#if ! defined(BOOST_HTTP_NO_POSIX_FADVISE)
24  
# if defined(__APPLE__) || (defined(__ANDROID__) && (__ANDROID_API__ < 21))
24  
# if defined(__APPLE__) || (defined(__ANDROID__) && (__ANDROID_API__ < 21))
25  
#  define BOOST_HTTP_NO_POSIX_FADVISE
25  
#  define BOOST_HTTP_NO_POSIX_FADVISE
26  
# endif
26  
# endif
27  
#endif
27  
#endif
28  

28  

29  
#if ! defined(BOOST_HTTP_USE_POSIX_FADVISE)
29  
#if ! defined(BOOST_HTTP_USE_POSIX_FADVISE)
30  
# if ! defined(BOOST_HTTP_NO_POSIX_FADVISE)
30  
# if ! defined(BOOST_HTTP_NO_POSIX_FADVISE)
31  
#  define BOOST_HTTP_USE_POSIX_FADVISE 1
31  
#  define BOOST_HTTP_USE_POSIX_FADVISE 1
32  
# else
32  
# else
33  
#  define BOOST_HTTP_USE_POSIX_FADVISE 0
33  
#  define BOOST_HTTP_USE_POSIX_FADVISE 0
34  
# endif
34  
# endif
35  
#endif
35  
#endif
36  

36  

37  
namespace boost {
37  
namespace boost {
38  
namespace http {
38  
namespace http {
39  
namespace detail {
39  
namespace detail {
40  

40  

41  
int
41  
int
42  
file_posix::
42  
file_posix::
43  
native_close(native_handle_type& fd)
43  
native_close(native_handle_type& fd)
44  
{
44  
{
45  
/*  https://github.com/boostorg/beast/issues/1445
45  
/*  https://github.com/boostorg/beast/issues/1445
46  

46  

47  
    This function is tuned for Linux / Mac OS:
47  
    This function is tuned for Linux / Mac OS:
48  

48  

49  
    * only calls close() once
49  
    * only calls close() once
50  
    * returns the error directly to the caller
50  
    * returns the error directly to the caller
51  
    * does not loop on EINTR
51  
    * does not loop on EINTR
52  

52  

53  
    If this is incorrect for the platform, then the
53  
    If this is incorrect for the platform, then the
54  
    caller will need to implement their own type
54  
    caller will need to implement their own type
55  
    meeting the File requirements and use the correct
55  
    meeting the File requirements and use the correct
56  
    behavior.
56  
    behavior.
57  

57  

58  
    See:
58  
    See:
59  
        http://man7.org/linux/man-pages/man2/close.2.html
59  
        http://man7.org/linux/man-pages/man2/close.2.html
60  
*/
60  
*/
61  
    int ev = 0;
61  
    int ev = 0;
62  
    if(fd != -1)
62  
    if(fd != -1)
63  
    {
63  
    {
64  
        if(::close(fd) != 0)
64  
        if(::close(fd) != 0)
65  
            ev = errno;
65  
            ev = errno;
66  
        fd = -1;
66  
        fd = -1;
67  
    }
67  
    }
68  
    return ev;
68  
    return ev;
69  
}
69  
}
70  

70  

71  
file_posix::
71  
file_posix::
72  
~file_posix()
72  
~file_posix()
73  
{
73  
{
74  
    native_close(fd_);
74  
    native_close(fd_);
75  
}
75  
}
76  

76  

77  
file_posix::
77  
file_posix::
78  
file_posix(
78  
file_posix(
79  
    file_posix&& other) noexcept
79  
    file_posix&& other) noexcept
80  
    : fd_(boost::exchange(other.fd_, -1))
80  
    : fd_(boost::exchange(other.fd_, -1))
81  
{
81  
{
82  
}
82  
}
83  

83  

84  
file_posix&
84  
file_posix&
85  
file_posix::
85  
file_posix::
86  
operator=(
86  
operator=(
87  
    file_posix&& other) noexcept
87  
    file_posix&& other) noexcept
88  
{
88  
{
89  
    if(&other == this)
89  
    if(&other == this)
90  
        return *this;
90  
        return *this;
91  
    native_close(fd_);
91  
    native_close(fd_);
92  
    fd_ = other.fd_;
92  
    fd_ = other.fd_;
93  
    other.fd_ = -1;
93  
    other.fd_ = -1;
94  
    return *this;
94  
    return *this;
95  
}
95  
}
96  

96  

97  
void
97  
void
98  
file_posix::
98  
file_posix::
99  
native_handle(native_handle_type fd)
99  
native_handle(native_handle_type fd)
100  
{
100  
{
101  
    native_close(fd_);
101  
    native_close(fd_);
102  
    fd_ = fd;
102  
    fd_ = fd;
103  
}
103  
}
104  

104  

105  
void
105  
void
106  
file_posix::
106  
file_posix::
107  
close(
107  
close(
108  
    system::error_code& ec)
108  
    system::error_code& ec)
109  
{
109  
{
110  
    auto const ev = native_close(fd_);
110  
    auto const ev = native_close(fd_);
111  
    if(ev)
111  
    if(ev)
112  
        ec.assign(ev,
112  
        ec.assign(ev,
113  
            system::system_category());
113  
            system::system_category());
114  
    else
114  
    else
115  
        ec = {};
115  
        ec = {};
116  
}
116  
}
117  

117  

118  
void
118  
void
119  
file_posix::
119  
file_posix::
120  
open(char const* path, file_mode mode, system::error_code& ec)
120  
open(char const* path, file_mode mode, system::error_code& ec)
121  
{
121  
{
122  
    auto const ev = native_close(fd_);
122  
    auto const ev = native_close(fd_);
123  
    if(ev)
123  
    if(ev)
124  
        ec.assign(ev,
124  
        ec.assign(ev,
125  
            system::system_category());
125  
            system::system_category());
126  
    else
126  
    else
127  
        ec = {};
127  
        ec = {};
128  

128  

129  
    int f = 0;
129  
    int f = 0;
130  
#if BOOST_HTTP_USE_POSIX_FADVISE
130  
#if BOOST_HTTP_USE_POSIX_FADVISE
131  
    int advise = 0;
131  
    int advise = 0;
132  
#endif
132  
#endif
133  
    switch(mode)
133  
    switch(mode)
134  
    {
134  
    {
135  
    default:
135  
    default:
136  
    case file_mode::read:
136  
    case file_mode::read:
137  
        f = O_RDONLY;
137  
        f = O_RDONLY;
138  
    #if BOOST_HTTP_USE_POSIX_FADVISE
138  
    #if BOOST_HTTP_USE_POSIX_FADVISE
139  
        advise = POSIX_FADV_RANDOM;
139  
        advise = POSIX_FADV_RANDOM;
140  
    #endif
140  
    #endif
141  
        break;
141  
        break;
142  
    case file_mode::scan:
142  
    case file_mode::scan:
143  
        f = O_RDONLY;
143  
        f = O_RDONLY;
144  
    #if BOOST_HTTP_USE_POSIX_FADVISE
144  
    #if BOOST_HTTP_USE_POSIX_FADVISE
145  
        advise = POSIX_FADV_SEQUENTIAL;
145  
        advise = POSIX_FADV_SEQUENTIAL;
146  
    #endif
146  
    #endif
147  
        break;
147  
        break;
148  

148  

149  
    case file_mode::write:
149  
    case file_mode::write:
150  
        f = O_RDWR | O_CREAT | O_TRUNC;
150  
        f = O_RDWR | O_CREAT | O_TRUNC;
151  
    #if BOOST_HTTP_USE_POSIX_FADVISE
151  
    #if BOOST_HTTP_USE_POSIX_FADVISE
152  
        advise = POSIX_FADV_RANDOM;
152  
        advise = POSIX_FADV_RANDOM;
153  
    #endif
153  
    #endif
154  
        break;
154  
        break;
155  

155  

156  
    case file_mode::write_new:
156  
    case file_mode::write_new:
157  
        f = O_RDWR | O_CREAT | O_EXCL;
157  
        f = O_RDWR | O_CREAT | O_EXCL;
158  
    #if BOOST_HTTP_USE_POSIX_FADVISE
158  
    #if BOOST_HTTP_USE_POSIX_FADVISE
159  
        advise = POSIX_FADV_RANDOM;
159  
        advise = POSIX_FADV_RANDOM;
160  
    #endif
160  
    #endif
161  
        break;
161  
        break;
162  

162  

163  
    case file_mode::write_existing:
163  
    case file_mode::write_existing:
164  
        f = O_RDWR | O_EXCL;
164  
        f = O_RDWR | O_EXCL;
165  
    #if BOOST_HTTP_USE_POSIX_FADVISE
165  
    #if BOOST_HTTP_USE_POSIX_FADVISE
166  
        advise = POSIX_FADV_RANDOM;
166  
        advise = POSIX_FADV_RANDOM;
167  
    #endif
167  
    #endif
168  
        break;
168  
        break;
169  

169  

170  
    case file_mode::append:
170  
    case file_mode::append:
171  
        f = O_WRONLY | O_CREAT | O_APPEND;
171  
        f = O_WRONLY | O_CREAT | O_APPEND;
172  
    #if BOOST_HTTP_USE_POSIX_FADVISE
172  
    #if BOOST_HTTP_USE_POSIX_FADVISE
173  
        advise = POSIX_FADV_SEQUENTIAL;
173  
        advise = POSIX_FADV_SEQUENTIAL;
174  
    #endif
174  
    #endif
175  
        break;
175  
        break;
176  

176  

177  
    case file_mode::append_existing:
177  
    case file_mode::append_existing:
178  
        f = O_WRONLY | O_APPEND;
178  
        f = O_WRONLY | O_APPEND;
179  
    #if BOOST_HTTP_USE_POSIX_FADVISE
179  
    #if BOOST_HTTP_USE_POSIX_FADVISE
180  
        advise = POSIX_FADV_SEQUENTIAL;
180  
        advise = POSIX_FADV_SEQUENTIAL;
181  
    #endif
181  
    #endif
182  
        break;
182  
        break;
183  
    }
183  
    }
184  
    for(;;)
184  
    for(;;)
185  
    {
185  
    {
186  
        fd_ = ::open(path, f, 0644);
186  
        fd_ = ::open(path, f, 0644);
187  
        if(fd_ != -1)
187  
        if(fd_ != -1)
188  
            break;
188  
            break;
189  
        auto const ev = errno;
189  
        auto const ev = errno;
190  
        if(ev != EINTR)
190  
        if(ev != EINTR)
191  
        {
191  
        {
192  
            ec.assign(ev,
192  
            ec.assign(ev,
193  
                system::system_category());
193  
                system::system_category());
194  
            return;
194  
            return;
195  
        }
195  
        }
196  
    }
196  
    }
197  
#if BOOST_HTTP_USE_POSIX_FADVISE
197  
#if BOOST_HTTP_USE_POSIX_FADVISE
198  
    if(::posix_fadvise(fd_, 0, 0, advise))
198  
    if(::posix_fadvise(fd_, 0, 0, advise))
199  
    {
199  
    {
200  
        auto const ev = errno;
200  
        auto const ev = errno;
201  
        native_close(fd_);
201  
        native_close(fd_);
202  
        ec.assign(ev,
202  
        ec.assign(ev,
203  
            system::system_category());
203  
            system::system_category());
204  
        return;
204  
        return;
205  
    }
205  
    }
206  
#endif
206  
#endif
207  
    ec = {};
207  
    ec = {};
208  
}
208  
}
209  

209  

210  
std::uint64_t
210  
std::uint64_t
211  
file_posix::
211  
file_posix::
212  
size(
212  
size(
213  
    system::error_code& ec) const
213  
    system::error_code& ec) const
214  
{
214  
{
215  
    if(fd_ == -1)
215  
    if(fd_ == -1)
216  
    {
216  
    {
217  
        ec = make_error_code(
217  
        ec = make_error_code(
218  
            system::errc::bad_file_descriptor);
218  
            system::errc::bad_file_descriptor);
219  
        return 0;
219  
        return 0;
220  
    }
220  
    }
221  
    struct stat st;
221  
    struct stat st;
222  
    if(::fstat(fd_, &st) != 0)
222  
    if(::fstat(fd_, &st) != 0)
223  
    {
223  
    {
224  
        ec.assign(errno,
224  
        ec.assign(errno,
225  
            system::system_category());
225  
            system::system_category());
226  
        return 0;
226  
        return 0;
227  
    }
227  
    }
228  
    ec = {};
228  
    ec = {};
229  
    return st.st_size;
229  
    return st.st_size;
230  
}
230  
}
231  

231  

232  
std::uint64_t
232  
std::uint64_t
233  
file_posix::
233  
file_posix::
234  
pos(
234  
pos(
235  
    system::error_code& ec) const
235  
    system::error_code& ec) const
236  
{
236  
{
237  
    if(fd_ == -1)
237  
    if(fd_ == -1)
238  
    {
238  
    {
239  
        ec = make_error_code(
239  
        ec = make_error_code(
240  
            system::errc::bad_file_descriptor);
240  
            system::errc::bad_file_descriptor);
241  
        return 0;
241  
        return 0;
242  
    }
242  
    }
243  
    auto const result = ::lseek(fd_, 0, SEEK_CUR);
243  
    auto const result = ::lseek(fd_, 0, SEEK_CUR);
244  
    if(result == (::off_t)-1)
244  
    if(result == (::off_t)-1)
245  
    {
245  
    {
246  
        ec.assign(errno,
246  
        ec.assign(errno,
247  
            system::system_category());
247  
            system::system_category());
248  
        return 0;
248  
        return 0;
249  
    }
249  
    }
250  
    ec = {};
250  
    ec = {};
251  
    return result;
251  
    return result;
252  
}
252  
}
253  

253  

254  
void
254  
void
255  
file_posix::
255  
file_posix::
256  
seek(std::uint64_t offset,
256  
seek(std::uint64_t offset,
257  
    system::error_code& ec)
257  
    system::error_code& ec)
258  
{
258  
{
259  
    if(fd_ == -1)
259  
    if(fd_ == -1)
260  
    {
260  
    {
261  
        ec = make_error_code(
261  
        ec = make_error_code(
262  
            system::errc::bad_file_descriptor);
262  
            system::errc::bad_file_descriptor);
263  
        return;
263  
        return;
264  
    }
264  
    }
265  
    auto const result = ::lseek(fd_, offset, SEEK_SET);
265  
    auto const result = ::lseek(fd_, offset, SEEK_SET);
266  
    if(result == static_cast<::off_t>(-1))
266  
    if(result == static_cast<::off_t>(-1))
267  
    {
267  
    {
268  
        ec.assign(errno,
268  
        ec.assign(errno,
269  
            system::system_category());
269  
            system::system_category());
270  
        return;
270  
        return;
271  
    }
271  
    }
272  
    ec = {};
272  
    ec = {};
273  
}
273  
}
274  

274  

275  
std::size_t
275  
std::size_t
276  
file_posix::
276  
file_posix::
277  
read(void* buffer, std::size_t n,
277  
read(void* buffer, std::size_t n,
278  
    system::error_code& ec)
278  
    system::error_code& ec)
279  
{
279  
{
280  
    if(fd_ == -1)
280  
    if(fd_ == -1)
281  
    {
281  
    {
282  
        ec = make_error_code(
282  
        ec = make_error_code(
283  
            system::errc::bad_file_descriptor);
283  
            system::errc::bad_file_descriptor);
284  
        return 0;
284  
        return 0;
285  
    }
285  
    }
286  
    std::size_t nread = 0;
286  
    std::size_t nread = 0;
287  
    while(n > 0)
287  
    while(n > 0)
288  
    {
288  
    {
289  
        // <limits> not required to define SSIZE_MAX so we avoid it
289  
        // <limits> not required to define SSIZE_MAX so we avoid it
290  
        constexpr auto ssmax =
290  
        constexpr auto ssmax =
291  
            static_cast<std::size_t>((std::numeric_limits<
291  
            static_cast<std::size_t>((std::numeric_limits<
292  
                decltype(::read(fd_, buffer, n))>::max)());
292  
                decltype(::read(fd_, buffer, n))>::max)());
293  
        auto const amount = (std::min)(
293  
        auto const amount = (std::min)(
294  
            n, ssmax);
294  
            n, ssmax);
295  
        auto const result = ::read(fd_, buffer, amount);
295  
        auto const result = ::read(fd_, buffer, amount);
296  
        if(result == -1)
296  
        if(result == -1)
297  
        {
297  
        {
298  
            auto const ev = errno;
298  
            auto const ev = errno;
299  
            if(ev == EINTR)
299  
            if(ev == EINTR)
300  
                continue;
300  
                continue;
301  
            ec.assign(ev,
301  
            ec.assign(ev,
302  
                system::system_category());
302  
                system::system_category());
303  
            return nread;
303  
            return nread;
304  
        }
304  
        }
305  
        if(result == 0)
305  
        if(result == 0)
306  
        {
306  
        {
307  
            // short read
307  
            // short read
308  
            return nread;
308  
            return nread;
309  
        }
309  
        }
310  
        n -= result;
310  
        n -= result;
311  
        nread += result;
311  
        nread += result;
312  
        buffer = static_cast<char*>(buffer) + result;
312  
        buffer = static_cast<char*>(buffer) + result;
313  
    }
313  
    }
314  
    return nread;
314  
    return nread;
315  
}
315  
}
316  

316  

317  
std::size_t
317  
std::size_t
318  
file_posix::
318  
file_posix::
319  
write(void const* buffer, std::size_t n,
319  
write(void const* buffer, std::size_t n,
320  
    system::error_code& ec)
320  
    system::error_code& ec)
321  
{
321  
{
322  
    if(fd_ == -1)
322  
    if(fd_ == -1)
323  
    {
323  
    {
324  
        ec = make_error_code(
324  
        ec = make_error_code(
325  
            system::errc::bad_file_descriptor);
325  
            system::errc::bad_file_descriptor);
326  
        return 0;
326  
        return 0;
327  
    }
327  
    }
328  
    std::size_t nwritten = 0;
328  
    std::size_t nwritten = 0;
329  
    while(n > 0)
329  
    while(n > 0)
330  
    {
330  
    {
331  
        // <limits> not required to define SSIZE_MAX so we avoid it
331  
        // <limits> not required to define SSIZE_MAX so we avoid it
332  
        constexpr auto ssmax =
332  
        constexpr auto ssmax =
333  
            static_cast<std::size_t>((std::numeric_limits<
333  
            static_cast<std::size_t>((std::numeric_limits<
334  
                decltype(::write(fd_, buffer, n))>::max)());
334  
                decltype(::write(fd_, buffer, n))>::max)());
335  
        auto const amount = (std::min)(
335  
        auto const amount = (std::min)(
336  
            n, ssmax);
336  
            n, ssmax);
337  
        auto const result = ::write(fd_, buffer, amount);
337  
        auto const result = ::write(fd_, buffer, amount);
338  
        if(result == -1)
338  
        if(result == -1)
339  
        {
339  
        {
340  
            auto const ev = errno;
340  
            auto const ev = errno;
341  
            if(ev == EINTR)
341  
            if(ev == EINTR)
342  
                continue;
342  
                continue;
343  
            ec.assign(ev,
343  
            ec.assign(ev,
344  
                system::system_category());
344  
                system::system_category());
345  
            return nwritten;
345  
            return nwritten;
346  
        }
346  
        }
347  
        n -= result;
347  
        n -= result;
348  
        nwritten += result;
348  
        nwritten += result;
349  
        buffer = static_cast<char const*>(buffer) + result;
349  
        buffer = static_cast<char const*>(buffer) + result;
350  
    }
350  
    }
351  
    return nwritten;
351  
    return nwritten;
352  
}
352  
}
353  

353  

354  
} // detail
354  
} // detail
355  
} // http
355  
} // http
356  
} // boost
356  
} // boost
357  

357  

358  
#endif
358  
#endif