OpenVPN 3 Core Library
Loading...
Searching...
No Matches
fileunix.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// Unix file read/write
13
14#pragma once
15
17
18#if defined(OPENVPN_PLATFORM_WIN)
19#error unix file methods not supported on Windows
20#endif
21
22#include <errno.h>
23#include <unistd.h> // for lseek
24#include <sys/types.h> // for lseek, open
25#include <sys/stat.h> // for open
26#include <fcntl.h> // for open
27#include <cstdint>
28
38
39namespace openvpn {
40OPENVPN_EXCEPTION(file_unix_error);
41
42// write binary buffer to file
43static constexpr mode_t WRITE_BINARY_UNIX_EXISTING = 010000; // special mode that is useful for writing /proc files
44inline void write_binary_unix(const std::string &fn,
45 const mode_t mode,
46 const std::uint64_t mtime_ns, // set explicit modification-time in nanoseconds since epoch, or 0 to defer to system
47 const void *buf,
48 const ssize_t size)
49{
50 // open
51 int flags = O_WRONLY | O_CLOEXEC;
52 if (!(mode & WRITE_BINARY_UNIX_EXISTING))
53 flags |= O_CREAT | O_TRUNC;
54 ScopedFD fd(::open(fn.c_str(), flags, mode & (WRITE_BINARY_UNIX_EXISTING - 1)));
55 if (!fd.defined())
56 {
57 const int eno = errno;
58 throw file_unix_error(fn + " : open for write : " + strerror_str(eno));
59 }
60
61 // write
62 if (size)
63 {
64 const ssize_t len = write_retry(fd(), buf, size);
65 if (len != size)
66 {
67 if (len == -1)
68 {
69 const int eno = errno;
70 throw file_unix_error(fn + " : write error : " + strerror_str(eno));
71 }
72 else
73 throw file_unix_error(fn + " : incomplete write, request_size=" + std::to_string(size) + " actual_size=" + std::to_string(len));
74 }
75 }
76
77 // explicit modification time
78 if (mtime_ns)
80
81 // close
82 {
83 const int eno = fd.close_with_errno();
84 if (eno)
85 throw file_unix_error(fn + " : close for write : " + strerror_str(eno));
86 }
87}
88
89inline void write_binary_unix(const std::string &fn,
90 const mode_t mode,
91 const std::uint64_t mtime_ns,
92 const Buffer &buf)
93{
94 write_binary_unix(fn, mode, mtime_ns, buf.c_data(), buf.size());
95}
96
97inline void write_binary_unix(const std::string &fn,
98 const mode_t mode,
99 const std::uint64_t mtime_ns,
100 const ConstBuffer &buf)
101{
102 write_binary_unix(fn, mode, mtime_ns, buf.c_data(), buf.size());
103}
104
105inline void write_text_unix(const std::string &fn,
106 const mode_t mode,
107 const std::uint64_t mtime_ns,
108 const std::string &content)
109{
110 write_binary_unix(fn, mode, mtime_ns, content.c_str(), content.length());
111}
112
113enum
114{ // MUST be distinct from BufferAllocated flags
115 NULL_ON_ENOENT = (1 << 8),
116};
117inline BufferPtr read_binary_unix(const std::string &fn,
118 const std::uint64_t max_size = 0,
119 const unsigned int buffer_flags = 0,
120 std::uint64_t *mtime_ns = nullptr)
121{
122 // open
123 ScopedFD fd(::open(fn.c_str(), O_RDONLY | O_CLOEXEC));
124 if (!fd.defined())
125 {
126 const int eno = errno;
127 if ((buffer_flags & NULL_ON_ENOENT) && eno == ENOENT)
128 return BufferPtr();
129 throw file_unix_error(fn + " : open for read : " + strerror_str(eno));
130 }
131
132 // get file timestamp
133 if (mtime_ns)
134 *mtime_ns = fd_mod_time_nanoseconds(fd());
135
136 // get file length
137 const off_t length = ::lseek(fd(), 0, SEEK_END);
138 if (length < 0)
139 {
140 const int eno = errno;
141 throw file_unix_error(fn + " : seek end error : " + strerror_str(eno));
142 }
143 if (::lseek(fd(), 0, SEEK_SET) != 0)
144 {
145 const int eno = errno;
146 throw file_unix_error(fn + " : seek begin error : " + strerror_str(eno));
147 }
148
149 // maximum size exceeded?
150 if (max_size && std::uint64_t(length) > max_size)
151 throw file_unix_error(fn + " : file too large [" + std::to_string(length) + '/' + std::to_string(max_size) + ']');
152
153 // allocate buffer
154 auto bp = BufferAllocatedRc::Create(size_t(length), buffer_flags);
155
156 // read file content into buffer
157 while (buf_read(fd(), *bp, fn))
158 ;
159
160 // check for close error
161 {
162 const int eno = fd.close_with_errno();
163 if (eno)
164 throw file_unix_error(fn + " : close for read : " + strerror_str(eno));
165 }
166
167 return bp;
168}
169
170// read file into a fixed buffer, return zero or errno
171template <typename STRING>
172inline int read_binary_unix_fast(const STRING &fn,
173 Buffer &out,
174 std::uint64_t *mtime_ns = nullptr)
175{
176 ScopedFD fd(::open(StringTempl::to_cstring(fn), O_RDONLY | O_CLOEXEC));
177 if (!fd.defined())
178 return errno;
179 if (mtime_ns)
180 *mtime_ns = fd_mod_time_nanoseconds(fd());
181 while (true)
182 {
183 const size_t remaining = out.remaining(0);
184 if (!remaining)
185 return EAGAIN; // note that we also return EAGAIN if buffer is exactly the same size as content
186 const ssize_t status = ::read(fd(), out.data_end(), remaining);
187 if (status == 0)
188 return 0;
189 else if (status < 0)
190 return errno;
191 out.inc_size(status);
192 }
193}
194
195inline std::string read_text_unix(const std::string &filename,
196 const std::uint64_t max_size = 0,
197 const unsigned int buffer_flags = 0,
198 std::uint64_t *mtime_ns = nullptr)
199{
200 BufferPtr bp = read_binary_unix(filename, max_size, buffer_flags, mtime_ns);
201 if (bp)
202 return buf_to_string(*bp);
203 else
204 return std::string();
205}
206} // namespace openvpn
const T * c_data() const
Returns a const pointer to the start of the buffer.
Definition buffer.hpp:1177
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1225
static Ptr Create(ArgsT &&...args)
Creates a new instance of RcEnable with the given arguments.
Definition make_rc.hpp:43
bool defined() const
Definition scoped_fd.hpp:58
#define OPENVPN_EXCEPTION(C)
const char * to_cstring(const std::string &str)
Support deferred server-side state creation when client connects.
Definition ovpncli.cpp:95
int update_file_mod_time_nanoseconds(const std::string &filename, const std::uint64_t nanoseconds_since_epooch)
Definition modstat.hpp:53
void write_binary_unix(const std::string &fn, const mode_t mode, const std::uint64_t mtime_ns, const void *buf, const ssize_t size)
Definition fileunix.hpp:44
int read_binary_unix_fast(const STRING &fn, Buffer &out, std::uint64_t *mtime_ns=nullptr)
Definition fileunix.hpp:172
@ NULL_ON_ENOENT
Definition fileunix.hpp:115
bool buf_read(const int fd, Buffer &buf, const std::string &title)
Definition bufread.hpp:29
ssize_t write_retry(int fd, const void *buf, size_t count)
Definition write.hpp:20
std::string read_text_unix(const std::string &filename, const std::uint64_t max_size=0, const unsigned int buffer_flags=0, std::uint64_t *mtime_ns=nullptr)
Definition fileunix.hpp:195
BufferPtr read_binary_unix(const std::string &fn, const std::uint64_t max_size=0, const unsigned int buffer_flags=0, std::uint64_t *mtime_ns=nullptr)
Definition fileunix.hpp:117
static constexpr mode_t WRITE_BINARY_UNIX_EXISTING
Definition fileunix.hpp:43
std::uint64_t fd_mod_time_nanoseconds(const int fd)
Definition stat.hpp:97
void write_text_unix(const std::string &fn, const mode_t mode, const std::uint64_t mtime_ns, const std::string &content)
Definition fileunix.hpp:105
RCPtr< BufferAllocatedRc > BufferPtr
Definition buffer.hpp:1859
std::string buf_to_string(const Buffer &buf)
Definition bufstr.hpp:22
std::string strerror_str(const int errnum)
Definition strerror.hpp:21
reroute_gw flags
static std::stringstream out
Definition test_path.cpp:10